3

I'm working with the reduce function on a super simple data set, and I'm getting NaN as the result when I didn't expect it:

let students = [{name: 'Leah', grade: 94},{name: 'Savannah', grade: 73},{name: 'Killian', grade: 38}];

let highest = students.reduce(
    (high, current) => Math.max(high.grade, current.grade)
    )

console.log(highest); // outputs NaN

The weird thing is, if I change the very first line to the following: (just taking out the third object)

let students = [{name: 'Leah', grade: 94},{name: 'Savannah', grade: 73}];

then this runs correctly and outputs 94.


Why does adding that one extra object cause problems?

1
  • 4
    Step through your code with the debugger. Each time through the loop, examine the values of high and current. You should be able to figure out the problem soon enough. Commented Jul 25, 2017 at 4:40

2 Answers 2

6

It all has to do with what is in the accumulator (high). If you don't provide the second argument to reduce, the accumulator starts off as the first object, and the current element is the second element. In your first iteration, you treat the accumulator as the object, fetching the grade using high.grade; but then you return a number (94), and not an object, to be your next accumulator. In the next iteration of the loop, high is not an object any more but 94, and (94).grade makes no sense.

When you remove the third element, there is no second iteration, and there is no time for the bug to occur, and you get the value of the current accumulator (94). If there were only one element, you'd get the initial accumulator ({name: 'Leah', grade: 94}). This is, obviously, not ideal, as you can't reliably predict the shape of the result of your calculation (object, number or error).

You need to decide whether you want the number or the object, one or the other.

let highest = students.reduce(
    (high, current) => Math.max(high, current.grade),
    Number.NEGATIVE_INFINITY
)

This variant keeps the accumulator as a number, and will return 94. We can't rely on the default starting accumulator, as it needs to be a number, so we set it artificially at -INF.

let highest = students.reduce(
    (high, current) => high.grade > current.grade ? high : current,
)

This is the object version, where highest ends up with {name: 'Leah', grade: 94}.

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

4 Comments

Thank you, should've seen that. Am I correct in saying that the example on this mozilla page would actually fail if one single more object was added? (The first example right under the Description heading)
yes, it'll return NaN if you add another object. You can test that easily in the console for verification.
@qarthandso For the first example right under the Description they say that you should use .map and an Initial value instead of the reduce() without initialValue examples. But they should also have included the failure case with three objects.
Indeed; I believe the example is there exactly to warn of this scenario (that without an initial value, you need to be very careful as there are several distinct scenarios that can play out). The example is exactly comparable to yours. When you don't specify the initial value, the type of the return value from the callback needs to match the type of the first element, or chaos ensues.
2

The issue here is the accumulator (high) after the first pass is a number (what gets returned by math.max), yet each pass requires that high be an object with grade as a number property. So by the second call you're calling Math.max(undefined, 73) - which will return NaN. Instead I'd recommend initializing the accumulator with -Infinity and only supplying high:

let highest = students.reduce( (high, current) => Math.max(high, current.grade) , -Infinity)

Comments

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.