将 _.groupBy 传递给 _.partialRight 似乎给出了不正确的结果
Passing _.groupBy to _.partialRight seems to give incorrect results
这是一个 JavaScript 对象,
const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
这是一个代码,它在两个数组 ojb.a
和 obj.b
、
的每个数组中按 .id
正确分组项目
const res1 = _.map(obj, x => _.groupBy(x, 'id'));
结果是
[
{
1: [{id: 1}, {id: 1}],
2: [{id: 2}],
3: [{id: 3}]
},
{
4: [{id: 4}],
5: [{id: 5}, {id: 5}],
6: [{id: 6}]
}
]
然而,lambda 实际上只是 _.groupBy
对其第二个参数的部分应用,它设置为 'id'
,所以我认为这样的事情应该有效,
const res2 = _.map(obj, _.partialRight(_.groupBy, 'id'));
或者至少是这样的
const res2 = _.map(obj, _.partialRight(_.groupBy, x => x.id));
然而,它们都不起作用,都导致了这个对象:
[
{
undefined: [{id: 1}, {id: 1}, {id: 2}, {id: 3}]
},
{
undefined: [{id: 4}, {id: 5}, {id: 5}, {id: 6}]
}
]
这是为什么?它是 lodash
中的错误吗?还是因为 JavaScript 的工作原理?在后一种情况下,发生了什么?
我找到了一个现有的 question + self-answer,它提供了使上述代码正常工作的解决方案:
const res2 = _.map(obj, _.ary(_.partialRight(_.groupBy, 'id'), 1));
然而我的部分问题仍未得到解答:为什么我需要使用 _.ary
?为什么我的初始尝试不起作用?
_.partialRight
方法仍然可以接受比新函数预期更多的参数。如果一个函数有两个参数,有一个或两个部分应用,那么任何 extra 参数有效地“抵消”部分应用的参数:
function print(a, b) {
console.log(a, b);
}
const f = _.partialRight(print, "world");
const g = _.partialRight(print, "hello", "world");
f("hi"); // hi world
g(); // hello world
f("hi", "universe"); // hi universe
g("greetings"); // greetings world
g("greetings", "universe"); // greetings universe
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
发生这种情况是因为 _.partialRight
有效地添加到 arguments
对象的末尾:
function print(a, b) {
console.log(...arguments);
}
const f = _.partialRight(print, "world");
const g = _.partialRight(print, "hello", "world");
f("hi"); // hi world
g(); // hello world
f("hi", "universe"); // hi universe world
g("greetings"); // greetings hello world
g("greetings", "universe"); // greetings universe hello world
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
因此_.partialRight
构造的函数容易受到the same problem that passing parseInt
as callback has - more arguments can be passed in and will be passed in, since the callback for _.map
的影响,总是传递元素、索引和数组。因此,即使 _.partialRight(_.groupBy, 'id')
应该将第二个参数设置为 'id'
,当 _.map
调用函数时 callback(item, index, array)
它会变成第四个参数。实际上执行的回调是
(item, index, array) => _.groupBy(item, index, array, 'id')
这就是为什么限制 arity with _.ary(fn, 1)
or directly with _.unary()
起作用的原因 - 来自 _.map()
的额外参数在这种情况下将被丢弃,只有第一个参数会被处理:
function print(a, b) {
console.log(a, b);
}
const f = _.unary(_.partialRight(print, "world"));
f("hi"); // hi world
f("hi", "universe"); // hi world
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
郑重声明,如果您更喜欢更实用的风格和 point-free style then you can use the Lodash FP Lodash 发行版,这会让这更容易。所有导出的函数都经过咖喱化处理,并且更改了参数,因此数据始终在最后。这使您可以更轻松地构建给定数据的处理:
const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
const process = _.map(_.groupBy("id"));
console.log(process(obj));
.as-console-wrapper { max-height: 100% !important; }
<script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script>
这是一个 JavaScript 对象,
const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
这是一个代码,它在两个数组 ojb.a
和 obj.b
、
.id
正确分组项目
const res1 = _.map(obj, x => _.groupBy(x, 'id'));
结果是
[
{
1: [{id: 1}, {id: 1}],
2: [{id: 2}],
3: [{id: 3}]
},
{
4: [{id: 4}],
5: [{id: 5}, {id: 5}],
6: [{id: 6}]
}
]
然而,lambda 实际上只是 _.groupBy
对其第二个参数的部分应用,它设置为 'id'
,所以我认为这样的事情应该有效,
const res2 = _.map(obj, _.partialRight(_.groupBy, 'id'));
或者至少是这样的
const res2 = _.map(obj, _.partialRight(_.groupBy, x => x.id));
然而,它们都不起作用,都导致了这个对象:
[
{
undefined: [{id: 1}, {id: 1}, {id: 2}, {id: 3}]
},
{
undefined: [{id: 4}, {id: 5}, {id: 5}, {id: 6}]
}
]
这是为什么?它是 lodash
中的错误吗?还是因为 JavaScript 的工作原理?在后一种情况下,发生了什么?
我找到了一个现有的 question + self-answer,它提供了使上述代码正常工作的解决方案:
const res2 = _.map(obj, _.ary(_.partialRight(_.groupBy, 'id'), 1));
然而我的部分问题仍未得到解答:为什么我需要使用 _.ary
?为什么我的初始尝试不起作用?
_.partialRight
方法仍然可以接受比新函数预期更多的参数。如果一个函数有两个参数,有一个或两个部分应用,那么任何 extra 参数有效地“抵消”部分应用的参数:
function print(a, b) {
console.log(a, b);
}
const f = _.partialRight(print, "world");
const g = _.partialRight(print, "hello", "world");
f("hi"); // hi world
g(); // hello world
f("hi", "universe"); // hi universe
g("greetings"); // greetings world
g("greetings", "universe"); // greetings universe
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
发生这种情况是因为 _.partialRight
有效地添加到 arguments
对象的末尾:
function print(a, b) {
console.log(...arguments);
}
const f = _.partialRight(print, "world");
const g = _.partialRight(print, "hello", "world");
f("hi"); // hi world
g(); // hello world
f("hi", "universe"); // hi universe world
g("greetings"); // greetings hello world
g("greetings", "universe"); // greetings universe hello world
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
因此_.partialRight
构造的函数容易受到the same problem that passing parseInt
as callback has - more arguments can be passed in and will be passed in, since the callback for _.map
的影响,总是传递元素、索引和数组。因此,即使 _.partialRight(_.groupBy, 'id')
应该将第二个参数设置为 'id'
,当 _.map
调用函数时 callback(item, index, array)
它会变成第四个参数。实际上执行的回调是
(item, index, array) => _.groupBy(item, index, array, 'id')
这就是为什么限制 arity with _.ary(fn, 1)
or directly with _.unary()
起作用的原因 - 来自 _.map()
的额外参数在这种情况下将被丢弃,只有第一个参数会被处理:
function print(a, b) {
console.log(a, b);
}
const f = _.unary(_.partialRight(print, "world"));
f("hi"); // hi world
f("hi", "universe"); // hi world
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.21/lodash.min.js"></script>
郑重声明,如果您更喜欢更实用的风格和 point-free style then you can use the Lodash FP Lodash 发行版,这会让这更容易。所有导出的函数都经过咖喱化处理,并且更改了参数,因此数据始终在最后。这使您可以更轻松地构建给定数据的处理:
const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
const process = _.map(_.groupBy("id"));
console.log(process(obj));
.as-console-wrapper { max-height: 100% !important; }
<script src="https://cdn.jsdelivr.net/g/lodash@4(lodash.min.js+lodash.fp.min.js)"></script>