Reduce

reduce is #3 in the trifecta of super useful array methods that I use all the time.

But that's underselling it. If there's something you wish you could do with map and filter, but you can't, reduce can do it.

With reduce, you can do pretty much anything you'd like with an array. In fact, you can write map and filter using reduce!

Let's start with a simple example:

[1,2,3,4].reduce((accumulator, element) => accumulator + element, 0)

This returns 10, the sum of 1, 2, 3, and 4.

reduce takes two parameters, a function and an accumulator. It then calls that function for each element.

For the first call, the accumulator (in our case, 0) and the first element is called. For the next call, the return value of the first call is passed in as the new accumulator.

The third call takes the return value of the second call as its accumulator, and so on.

The return value of the last call is what reduce actually returns.

Here's how you'd implement map using reduce:

var map = (func, arr) =>  
    arr.reduce(
        (acc, el) => acc.concat([func(el)]),
        [])
console.log(map((x) => 2*x, [1, 2, 3, 4]))  

This prints [ 2, 4, 6, 8 ] like you'd expect an array's map method to do.

BTW, concat is an array method that takes another array and returns an new array, the first part being what's in the original array, with the remaining part being what was in the the array-parameter to concat.

All we're doing here is making the new array that map returns our accumulator.

Here's filter:

var filter = (func, arr) =>  
    arr.reduce(
        (acc, el) => func(el) ? acc.concat([el]) : acc,
        [])
console.log(filter((x) => x % 2 === 0, [1, 2, 3, 4]))  

This prints, as you would expect, [ 2, 4 ].

Ok, here's one last example to show you reduce's flexibility. Let's say you've got a list of heroes:

var heroes = [  
    {
        name: 'Dontatello',
        type: 'Ninja'
    },
    {
        name: 'Brian',
        type: 'Ninja'
    },
    {
        name: 'Optimus Primal',
        type: 'Robot'
    },
    {
        name: 'Dinobot',
        type: 'Robot'
    },
    {
        name: 'Superman',
        type: 'Alien'
    },
    {
        name: 'Martian Manhunter',
        type: 'Alien'
    }
]

And that you want to list them by type. Here's one way to do it:

var heroes_by_type = (heroes) =>  
    heroes.reduce((acc, hero) => {
        if(acc[hero.type]) {
            acc[hero.type].push(hero.name)
        } else {
            acc[hero.type] = [hero.name]
        }
        return acc
    }, {})

Functional programmers will notice I'm doing a bit of a no-no. I'm mutating the accumulator. I'll get to that in a later post. :)

Looking for a software developer?