reduce: 3 things go in, whatever you want comes out

The reduce function takes an Array, a function to modify the data, and a starting value. The function gets each item in the Array and the current value of the accumulator. Whatever you return in the function will update the accumulator value. You don't have to update the accumulator at all if you don't want to.

Reduce is the Squat of the big 3. Unlike map and filter, he might not even return an Array; he could return anything. Don't stress if reduce is challenging for months. Other languages will sometimes call it "fold" instead of "reduce".

Reduce also has a special name for the function you give it called a reducer. If you've learned any Redux, then you'll immediately recognize it. The Redux framework is a bunch of reducer functions.

[jwarden 1.8.2019] TODO: I like this example, but it's pretty advanced. Perhaps people skipping around might want something simpler. We should put at least 2 here; one showing the Array return, and the this one showing the String return.

Old Sk00l for

Let's accumulate using the traditional for loop. We want to iterate over a list of party members and create a formatted String summarizing what is in the list.

const party = [
    {
        name: 'Jesse',
        clazz: 'Swashbuckler',
        hitPoints: 20,
        maxHitPoints: 22
    },
    {
        name: 'Brandy',
        clazz: 'Cleric',
        hitPoints: 14,
        maxHitPoints: 14
    },
    {
        name: 'Albus',
        clazz: 'War Dog',
        hitPoints: 5,
        maxHitPoints: 9
    }
]

let summary = ''
for(i = 0; i < party.length; i++) {
    const member = party[i]
    summary += `${member.name} \t- ${member.hitPoints}/${member.maxHitPoints}\n`
}
console.log(summary)

The summary ends up looking like:

Jesse   - 20/22
Brandy  - 14/14
Albus   - 5/9

reduce

We can purify that by using reduce. First, our reduce function:

const addMemberToSummary = (summary, member) =>
    `${summary}${member.name} \t- ${member.hitPoints}/${member.maxHitPoints}\n`

Let's test it out:

addMemberToSummary(
    '', 
    {
        name: 'Jesse',
        clazz: 'Swashbuckler',
        hitPoints: 20,
        maxHitPoints: 22
    })
)
// Jesse   - 20/22

Nice, now we'll use that function in reduce below:

const partyToString = party =>
    reduce(
        party,
        addMemberToSummary,
        ''
    )
partyToString(party)

Gives us:

Jesse   - 20/22
Brandy  - 14/14
Albus   - 5/9

The addMemberToSummary will be called for each item of the Array. The reduce function will give the accumulator as the first argument, and the item in the Array as the 2nd. Since it returns a String, the accumulator will be set to that string. Notice while we're returning a new String each time, we ensure it's appended to the accumulator by injecting it in the front; whatever we add always goes at the end.

What was summary in the for loop example is our 3rd argument to the reduce function. The key difference is in our for loop:

  • it was a variable
  • it was mutated each loop
  • we had to store it

Using reduce:

  • it's an immutable constant
  • your function returns a new value to set it to for the next function call
  • reduce stores it

Conclusions

Reduce: Take an Array of things in as well as an accumulator, give the current value of the accumulator and the current item in the Array to the reduce function, whatever that function returns will be set as the new value for the accumulator. Whatever the accumulator value is after the last item in the Array has been iterated over, that's what is returned from the function. The reduce function expects a pure reducer function, and is itself a pure function.

It's called "accumulator" because it's assumed to accumulate, or "collect" values over time, but that isn't always the case. You can return whatever you want from your reducer function.

Unlike map and filter, the reduce function can take long while to wrap your head around.

Map... change stuff. Filter... filter stuff. Reduce... uh... convert, or... uh....

Additionally, you can still struggle to remember exactly how it works for a long time, and continually look up the documentation on parameter order, and the reducer function parameter order. Also, the accumulator can be anything (a Number for adding numbers, another Array for doing a more complicated map, etc) so it can take time to find creative uses from your own exploration. Give yourself some slack; this is the hardest of The Big 3 list comprehensions to memorize, use, and get proficient with.

results matching ""

    No results matching ""