Here's a JavaScript object,
const obj = {a: [{ id: 1 }, {id: 1}, {id: 2}, {id: 3}], b: [{ id: 4 }, {id: 5}, {id: 5}, {id: 6}] };
and here's a code that correctly groups the items by .id in each of the two arrays ojb.a and obj.b,
const res1 = _.map(obj, x => _.groupBy(x, 'id'));
the result being
[
{
1: [{id: 1}, {id: 1}],
2: [{id: 2}],
3: [{id: 3}]
},
{
4: [{id: 4}],
5: [{id: 5}, {id: 5}],
6: [{id: 6}]
}
]
The lambda, however, is in fact just the partial application of _.groupBy to its second argument, which is set to 'id', so I thought something like this should work,
const res2 = _.map(obj, _.partialRight(_.groupBy, 'id'));
or at least something like this
const res2 = _.map(obj, _.partialRight(_.groupBy, x => x.id));
however, neither of them works, both resulting in this object:
[
{
undefined: [{id: 1}, {id: 1}, {id: 2}, {id: 3}]
},
{
undefined: [{id: 4}, {id: 5}, {id: 5}, {id: 6}]
}
]
Why is that? Is it a bug in lodash? Or is it because of how JavaScript works? In the latter case, what's happening?
I've found an existing question + self-answer which gives a solution for making the code above work:
const res2 = _.map(obj, _.ary(_.partialRight(_.groupBy, 'id'), 1));
However part of my question is still not answerd: why do I need to use _.ary? Why doesn't my initial attempt work?
The
_.partialRightmethod can still accept more arguments than the new function should expect. If a function takes two arguments, has one or two partially applied, then any extra arguments effectively "bump off" the partially applied ones:This happens because
_.partialRighteffectively adds to the end of theargumentsobject:Thus the function constructed by
_.partialRightis susceptible to the same problem that passingparseIntas callback has - more arguments can be passed in and will be passed in, since the callback for_.mapalways passes the element, the index, and the array. Therefore even though_.partialRight(_.groupBy, 'id')should have the second argument set to'id', when_.mapcalls the function ascallback(item, index, array)it turns into the fourth argument. And effectively the callback that gets executed isThis is why clamping down the arity with
_.ary(fn, 1)or directly with_.unary()works - the extra arguments from_.map()would be discarded in that case and only the first one would be processed:For the record, if you prefer more functional style and point-free style then you can use the Lodash FP distribution of Lodash which makes this easier. All the exported functions are curried and the arguments are changed so data is always last. Which allows you to more easily construct the processing for given data: