TPJ
TPJ

Reputation: 291

Weird behaviour of map and max in lodash

var a = [ [1,2,3], [4,5,6], [7,8,9] ];
_.map( a, _.max );
// gives [3, -Infinity, -Infinity]

I have tested it on my Chrome browser, on the lodash site.

Shouldn't the code above return [3, 6, 9]?

I can get the correct result with forEach:

var a = [ [1,2,3], [4,5,6], [7,8,9] ];
var result = [];
_.forEach( a, function(arr) { result.push(_.max(arr)); } );

Upvotes: 3

Views: 2695

Answers (1)

ComFreek
ComFreek

Reputation: 29434

Overall conclusion

I've opened an issue on GitHub: https://github.com/lodash/lodash/issues/379
Thanks to jdalton, the issue is now fixed. Sample: → jsFiddle

Details (why is the result 'weird'?)

I have never worked with Lodash in detail, but here are my researches:

Retrieves the maximum value of a collection. If the collection is empty or falsey -Infinity is returned.
Lodash Documentation: _.max()

Testing _.max() with each group invidually worked perfectly:

_.max([1,2,3]);  // 3
_.max([4,5,6]);  // 6
_.max([7,8,9]);  // 9

Now, I tried calling _.max() manually in the callback function of _.map():

var a = [ [1,2,3], [4,5,6], [7,8,9] ];
alert(_.map( a, function(val) {
    return _.max(val);
}));

Works fine! So where is the difference between that and supplying _.max() as the second parameter?
_.map() actually sends 3 parameters to the callback function:

(value, index|key, collection).
Lodash Documentation: _.map()

Consider the second parameter of _.max() now:

2.[callback=identity] (Function|Object|string): The function called per iteration. If a property name or object is provided it will be used to create a ".pluck" or ".where" style callback, respectively.
Lodash Documentation: _.max()

Conclusion: _.max() gets also passed the second and third parameter supplied by _.map(). The second parameter is important here! Passing truthy values (e.g. integers != 0) for it lets the function return -Infinity.

Test cases (→ jsFiddle):

alert(_.max([1,2,3], 0)); // 3
alert(_.max([1,2,3], 1)); // -Infinity
alert(_.max([1,2,3], 2)); // -Infinity

That is coming from the boolean checks performed in the source code: https://github.com/lodash/lodash/blob/f0f7eee963966516490eb11232c9e9b4c6d0cc6c/dist/lodash.js#L3431

Because callback (the second parameter) is a truthy value, we will directly jump into the else branch. There, callback is reassigned as the following (the ternary operation will also take the else branch):

lodash.createCallback(callback, thisArg, 3);

createCallback() is defined here. It returns the following function for our specific input parameters (these are 1, null, 3, see _.max() for details):

return function(object) {
  return object[func];
};

Let's say, we save that in a variable called callback (→ jsFiddle):

var callback = _.createCallback(1, null, 3);

Calling that function with object = 1 (or 2, 3 or 6, etc.) results in undefined (that's pretty obvious).

Going back to _.max(), we see that there is a loop which compares the current value (from the callback function) to the initial/last value, and that is -Infinity as set at the beginning of the function.

undefined > -Infinity never results in true, so -Infinity will stay the 'maximum' value.

Upvotes: 5

Related Questions