3

Why does the second example return NaN, whereas the first one works?

const numbers = [ 1, 2, 3 ]

console.log('passing arrow funciton', numbers.reduce((l, r) => Math.max(l, r)) ) // 3

console.log('passing bound function', numbers.reduce(Math.max.bind(Math)) ) // NaN

To give you some context the reduce function requires a Callback param. Furthermore, the callback requires two params, the Accumulation and Current element ( you can also call them left and right etc. ). There are more parameters but they are optional.

I've tried to mock the reduce function and the example works like a charm

const numbers = [1, 2, 3]

const reduce = (array, func) => {

  let largest = 0

  array.forEach
  (
    (number, i, a) =>
      a[i + 1]
      ? largest = func(number, a[i + 1])
      : console.log('done')
  )

  return largest

}

console.log( 'mock reduce', reduce(numbers, Math.max.bind(Math)) ) // 3

enter image description here

5
  • 3
    Isn't this over-complicating Math.max(...numbers)? Commented Aug 22, 2018 at 11:59
  • 1
    You are right, it's simpler and easier to do spread the arguments, but that's not the point. This is somewhat of an anomaly and usually you can learn some very useful things by trying to make sense of an unexpected behavior. Commented Aug 22, 2018 at 12:20
  • @MatejaPetrović you misunderstand my answer. The .reduce() function passes 4 parameters to the callback, and one of those parameters is not a number. Commented Aug 22, 2018 at 12:21
  • Right. Note that the last parameter is the array itself, and that will be NaN when Math.max() tries to convert it to a number. The documentation says that it's "optional", but that means that your callback function can ignore it, which is exactly what your first callback function does. Commented Aug 22, 2018 at 12:25
  • You can see this for yourself if you run .reduce() and console.log(arguments) in the callback. Commented Aug 22, 2018 at 12:28

1 Answer 1

6

The callback to .reduce() is passed 4 parameters: the accumulator, the current element, the index, and the overall array. The NaN is coming from the attempt to convert the array itself to a number.

On the first call to the callback, therefore, .max() will be passed 1, 2, 1, [1, 2, 3]. That'll return NaN, and it'll then be NaN all the way. Besides the problem of the NaN from the array, the index value could also throw off the accuracy of the result if you had a long array with lots of small or negative numbers.

Note that what I'm describing here is the argument list that .reduce() passes to the callback function provided, not the arguments to .reduce() itself. The .reduce() function always passes all four parameters to the callback, but the nature of JavaScript is that functions can ignore excess parameters. That's exactly what your first example does.

Sign up to request clarification or add additional context in comments.

6 Comments

Thank you for your answer but I believe you wanted to say that the first argument of reduce is a callback function which takes the accumulator the current element ( the current index and the array which is being iterated over ). Whereas the second argument to reduce is the initial value which if not specified is going to be the first item in the array. You can find the information here: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… Anyhow, I don't see how this solves the issue, can you post a working example, please?
@MatejaPetrović No I'm talking about the arguments passed into the callback function.
@MatejaPetrović sorry I have not had enough coffee this morning. Answer edited; it's 4 parameters, not 3.
I get what you are trying to say. At first I thought it's odd that the reduce would try to cram in an optional array parameter into Math.max ( along with the index ). But since Math.max is greedy it does make sense.
@MatejaPetrović there are other similar situations with trying to use built-in functions as callbacks. It's an unfortunate pattern in JavaScript.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.