DEV Community

Warren
Warren

Posted on

How to use reduce in javascript

How to use reduce in javascript

Reduce is one of those functions that seems to get a marmite reaction. Some people love it, and others hate it.
I'm primarily a .NET developer and am a big fan of LINQ when it comes to collections. The two most commonly used methods from this library are likely to be Select and Where, which in JavaScript correspond to map and filter, and are used in pretty much the same way.

const values = [1, 2, 3, 4]
var doubled = values.Select(x => 2*x); // returns [2, 4, 6, 8]
var odds = values.Where(X => x % 2 != 0); // returns [1, 3]
Enter fullscreen mode Exit fullscreen mode
const values = [1, 2, 3, 4]
const doubled = values.map(x => 2*x) // returns [2, 4, 6, 8]
const odds = values.filter(x => x % 2 !== 0) // returns [1, 3]
Enter fullscreen mode Exit fullscreen mode

But when I first came across reduce I realised I didn't know what the LINQ equivalent was. It's Aggregate btw, but the reason I didn't know is because I had simply never needed it. This isn't because this type of function is useless, but because LINQ provides a host of other more specific aggregate functions, especially if you also use MoreLINQ as we do.

Useful Aggregates

The sort of aggregate functions I immediately started to use reduce for were things like Sum, Min, Max, Distinct, etc.

The same result can usually be achieved by using a forEach loop and there's no reason why you can't. My preference for using reduce is that the code often looks very similar, but is still a pure function that doesn't rely on mutable variables.

Sum

Consider these approaches to adding an array of numbers using forEach and reduce (there will be a full explanation of the code in the next section).

forEach

let total = 0;
values.forEach(x => {
    total += x
})
Enter fullscreen mode Exit fullscreen mode

reduce

const total = values.reduce((prev, curr) => {
    return prev + curr
}, 0)
Enter fullscreen mode Exit fullscreen mode

The forEach depends on a variable value which can be changed, and wraps this in a closure allowing it to be progressively added to, where the reduce implementation is a pure function the result of which goes directly in to an immutable constant.

Reduce

The reduce function takes in two arguments

  • The reducer
  • An optional initial value

The reducer is the part that confuses most people. The reducer is a function that will perform the aggregation one value at a time. If you've seen the MDN documentation then you know that the reducer can accept up to 4 parameters, but typically you only need the first two. I always call these two parameters prev, and curr. It's worth noting though that prev is not the previous value in the array but the previous value returned by the reducer. Continuing with summing as an example:

Sum

const values = [1, 2, 3, 4]
const reducer = (prev, curr) => {
    return prev + curr
}
const total = values.reduce(reducer, 0)
Enter fullscreen mode Exit fullscreen mode

I've extracted the reducer into a separate variable just to make it clearer which part of the above I'm talking about. This reducer function will be called once for each value in the array.

The first time we come in prev takes the value of the second parameter passed to reduce, in this case 0 (If we didn't specify an initial value it would be undefined). curr would be the first value from the array. It adds the two and returns the result. The next time the reducer is called this result will become the prev value. See the table below for what happens to each parameter is it loops through the array.

Loop # prev value curr value Returned value
1 0 1 1
2 1 2 3
3 3 3 6
4 6 4 10

The final result 10 would be returned from the reduce function and stored in the total constant.

Max

Another example, this time we'll find the greatest number in an array of numbers.

const values = [15, 6, 12, 24, 3, 11]
const max = values.reduce((prev, curr) => {
    return prev > curr ? prev : curr
})
Enter fullscreen mode Exit fullscreen mode

This time our table of values will look like:

Loop # prev value curr value Returned value
1 undefined 15 15
2 15 6 15
3 15 12 15
4 15 24 24
5 24 3 24
6 24 11 24

With 24 as our final result.

Aggregates with different types to the array

So far the return type of our reducer has been the same as the input types, which means that both prev and curr parameters have also been of the same type, but this is not always the case.

In this example we'll convert an array of objects into a javascript object. This can be useful for using it as a dictionary.

const values = [
    {id: 106, name: "Wibble"},
    {id: 357, name: "Wobble"},
    {id: 652, name: "Flibble"}
]

const valuesDictionary = values.reduce((prev, curr) => {
    return {
        ...prev,
        [curr.id]: curr
    }
}, {})

console.log(valuesDictionary[652]) // outputs "{id: 652, name: "Flibble"}"
Enter fullscreen mode Exit fullscreen mode

This example makes use of the spread operator to take the properties of the prev parameter and add them all to the new object the reducer returns. The end result is a JS object which you can use as a dictionary to look up each item by its id.

The above achieves the same result as .NET's ToDictionary method.

Top comments (0)