{ Reduce. }

Objectives:

By the end of this chapter, you should be able to:

  • Understand what reduce does and the parameters it accepts
  • Reduce values into different kinds of data types
  • Compare and contrast reduce and reduceRight

reduce

reduce is one of the most powerful iterators, but it has a bit of a steeper learning curve. The purpose of reduce is to take an array and reduce it to a single value. What makes reduce so powerful is that the value you reduce can be anything (a string, boolean, object, even a 2D array!).

The parameters to the callback of reduce (as well as the function itself) are a bit different than forEach, map and filter. Unlike the other iterators we've seen, the callback to reduce takes four arguments, not three. The second through fourth arguments are the same as the ones we've seen before (value, index, array). However, the first parameter is one we haven't seen before.

Depending on where you read documentation, you will see the first parameter of reduce being called start, previous, or accumulator. Here's how it works: at each step in the iteration, this value is whatever was returned by the callback in the previous iteration. For the first iteration (when there is no previous value), you can pass in an initial value as the second parameter to reduce. If you don't supply an argument, reduce will assume that the value of this parameter should be the first value in the array, and it will start the iteration from the second element.

Here are some examples. In general, the best way to understand reduce is to create a snippet, set some break points, and watch how the arguments in the callback change with each iteration. Try it out!

let arr = [2,4,6,8];
arr.reduce(function(acc,next){
    return acc + next;
},5);

/*
In the first iteration, acc is 5 and next is 2; the callback returns 5 + 2 = 7.
In the second iteration, acc is 7 and next is 4; the callback returns 7 + 4 = 11.
In the third iteration, acc is 11 and next is 6; the callback returns 11 + 6 = 17.
In the last iteration, acc is 17 and next is 8; the callback returns 17 + 8 = 25.
Now the array is exhausted, so reduce returns 25 (which is the sum of all elements in the array, plus 5)
*/

let arr = [2,4,6,8];
arr.reduce(function(acc,next){
    return acc + next;
});

// 20 (When no second argument is supplied to reduce, the `acc` starts at the first value in the array, i.e. 2. In this case, we simply get the sum of all values in the array.)

You can obtain different data types from reducing by manipulating the initial value of the accumulator. Here's an example where an array is reduced to an object:

let arr = [1,2,3,4];
arr.reduce(function(acc,next){
    if(next >= 2){
        acc['key' + next] = next;
    }
    return acc;
},{});

// Think about what acc and next are for each step in the iteration!
// Ultimately, this reduce will return the following:
// Object {key2: 2, key3: 3, key4: 4}

Once again, the best way to understand these examples is to create a snippet in the sources tab in Chrome dev tools, and use the keyword debugger or set breakpoints on each line of the callback function. If you can understand how to use reduce, you will have a much much easier time solving difficult problems with far less code.

Let's try a few exercises using reduce. Many of these should look familiar - we are doing them again to highlight that almost anything you can do with forEach, map, or filter, you can do with reduce!

Exercises

Write a function called extractKey which accepts two parameters, an array of objects, and the name of a key and returns an array with just the values for that key:

extractKey([{name: "Elie", isInstructor:true},{name: "Tim", isInstructor:true},{name: "Matt", isInstructor:true}], "name");

// ["Elie", "Tim", "Matt"]

Write a function called filterLetters which accepts an array of letters and returns the number of occurrences of a specific letter. This function should be case insensitive

filterLetters(["a","a","b","c","A"], "a"); // 3
filterLetters(["a","a","b","c","A"], "z"); // 0
filterLetters(["a","a","b","c","A"], "B"); // 1

Optional Bonus

Write a function called addKeyAndValue which accepts three parameters, an array (of objects), a key and a value. This function should return the array of objects after each key and value has been added. You can do this a few ways, either by reducing starting with an empty array and making copies of the object or by starting with the actual array!

addKeyAndValue([{name: 'Elie'},{name: 'Tim'},{name: 'Elie'}], "isInstructor", true);

/*
[
    {
        name: 'Elie',
        isInstructor: true
    },
    {
        name: 'Tim',
        isInstructor: true
    },
    {
        name: 'Elie',
        isInstructor: true
    }
]
*/

You can read more about reduce here.

reduceRight

Relatedly, you can also reduce an array starting from the end and moving backwards, rather than starting from the front and moving forwards. To do this you can use the reduceRight iterator. You can read more about reduceRight here.

When you're ready, move on to Additional Array Methods

Continue

Creative Commons License