Execute Program

JavaScript Arrays: Reduce

Welcome to the Reduce lesson!

JavaScript arrays' reduce method combines the array's elements to produce a new value. Its most common use is to sum numbers.

This lesson is shown as static text below. However, it's designed to be used interactively. Click the button below to start!

  • Sometimes we want to repeatedly apply a function to each element in a list. For example, we might want to sum a list of numbers. This is simple if we know how many numbers there will be, and there are only a few of them.

  • >
    function add(a, b) {
    return a + b;
    }
    add(1, 20);
    Result:
    21Pass Icon
  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    add(add(1, 20), 300);
    Result:
    321Pass Icon
  • We can use a loop to apply this function to every number in an array.

  • Note: this code example reuses elements (variables, etc.) defined in earlier examples.
    >
    const nums = [1, 20, 300, 4000];
    let sum = 0;
    nums.forEach(n => {
    sum = add(sum, n);
    });
    sum;
    Result:
    4321Pass Icon
  • The built-in .reduce method simplifies this by running a function on each array element, keeping track of the result. It "reduces" the array to one return value. In this case, that value is the sum of its elements.

  • We'll see .reduce in action first, then discuss how it works. The next example uses .reduce to sum an array of numbers, just like we did above.

  • >
    [1, 20, 300, 4000].reduce(
    (sum, current) => sum + current,
    0
    );
    Result:
    4321Pass Icon
  • We pass two arguments to .reduce. The first is a function that adds a new number to our running sum, returning the new sum. The second argument is 0, the initial value for the sum.

  • The callback function is called once for each number in the array. During each call, it adds the current number to the running sum, then returns that new sum. We can see this in action by instrumenting our function to store all of the sums. (The verb "instrument" means "attach measurement instruments to". It's a great way to learn how unfamiliar systems work!)

  • >
    const intermediateSums = [];

    [1, 20, 300].reduce(
    (sum, current) => {
    sum = sum + current;
    intermediateSums.push(sum);
    return sum;
    },
    0
    );

    intermediateSums;
    Result:
  • >
    const intermediateSums = [];

    [1, 20, 300, 4000].reduce(
    (sum, current) => {
    sum = sum + current;
    intermediateSums.push(sum);
    return sum;
    },
    0
    );

    intermediateSums;
    Result:
    [1, 21, 321, 4321]Pass Icon
  • We can make our .reduce call shorter by omitting the second argument. In that case, the first array element is used as the sum. This works for many use cases of .reduce, including computing sums.

  • >
    [1, 20, 300].reduce((sum, current) => sum + current);
    Result:
    321Pass Icon
  • When we omit the second argument, our function is never called for array element 0. To sum [1, 20, 300], this happens:

    • sum is set to element 0 of the array, which is 1.
    • callback(1, 20) is called, returning 21 as the new sum.
    • callback(21, 300) is called, returning 321 as the new sum.
    • There are no more array elements, so .reduce returns 321.
  • >
    const intermediateSums = [];

    [1, 20, 300, 4000].reduce(
    (sum, current) => {
    sum = sum + current;
    intermediateSums.push(sum);
    return sum;
    }
    );

    intermediateSums;
    Result:
    [21, 321, 4321]Pass Icon
  • .reduce isn't limited to numbers. In the next example, we have users with names and numerical points used to score a game. We reduce the users array to the sum of points for all users.

  • The second argument to the callback function still refers to the current array element. Now this element is a user object, so we name it user instead of current.

  • >
    const users = [
    {name: 'Amir', points: 8},
    {name: 'Betty', points: 12},
    {name: 'Cindy', points: 4},
    ];

    const pointTotal = users.reduce((sum, user) => {
    return sum + user.points;
    }, 0);

    pointTotal;
    Result:
    24Pass Icon
  • For that example to work, we had to provide the initial value of 0. If we omit the initial value argument, .reduce will try to use the user object at index 0 as the initial value.

  • Adding an object to a number causes the object to be converted into a string, '[object Object]', which isn't what we want. Things only get worse from there.

  • >
    const users = [
    {name: 'Amir', points: 8},
    {name: 'Betty', points: 12},
    {name: 'Cindy', points: 4},
    ];

    const pointTotal = users.reduce((sum, user) => {
    return sum + user.points;
    });

    pointTotal;
    Result:
  • Here's a closer view of what happened to produce that string:

  • >
    const user = {name: 'Amir', points: 8};
    user + 12 + 4;
    Result:
    '[object Object]124'Pass Icon
  • We can use .reduce to emulate the behavior of .join, which works on strings.

  • >
    ['x', 'y', 'z'].join('a');
    Result:
    'xayaz'Pass Icon
  • >
    ['x', 'y', 'z'].reduce((joined, current) => joined + 'a' + current);
    Result:
    'xayaz'Pass Icon
  • (The first argument of our callback function here is joined, not sum. It would be strange to call it "sum", since we're no longer adding.)

  • We can write our own general-purpose .join using .reduce. It adds the separator we provide before each element. Note that we skip the second argument to .reduce, so that separator isn't added before the first element

  • >
    function join(array, separator) {
    return array.reduce(
    (joined, current) => joined + separator + current);
    }
    join(['x', 'y', 'z'], '_');
    Result:
    'x_y_z'Pass Icon
  • So far, we've named the callback's first argument sum and joined. From now on, we name the callback's first argument acc. acc stands for "accumulator", because it's accumulating the result. Sometimes there are obvious better names, like sum or joined. But for some examples, there's no obvious good name.

  • If one of the next few examples gives you trouble, try writing it out as a table. At each step in the .reduce, what are the values of acc and current? Remember that .reduce does little more than call the provided function over and over.

  • >
    [1, 200, 30].reduce((acc, current) => Math.max(acc, current));
    Result:
    200Pass Icon
  • >
    [1, 200, 30, 4000].reduce((acc, current) => Math.max(acc, current));
    Result:
    4000Pass Icon
  • >
    [true, false, true].reduce((acc, current) => acc && current);
    Result:
    falsePass Icon
  • >
    [true, false, true].reduce((acc, current) => acc || current);
    Result:
    truePass Icon
  • >
    [false, false, false].reduce((acc, current) => acc || current);
    Result:
    falsePass Icon
  • >
    [[1], [2, 3], [4]].reduce((acc, current) => acc.concat(current));
    Result:
    [1, 2, 3, 4]Pass Icon
  • What happens when we try to reduce an empty array? If we provide an initial value as the second argument to .reduce, then .reduce will simply return that value. If the initial value is 0, we'll get 0 out.

  • >
    [].reduce(
    (sum, current) => sum + current,
    0
    );
    Result:
    0Pass Icon
  • If we don't provide an initial value, .reduce has no data to work with, so it errors.

  • >
    [].reduce(
    (sum, current) => sum + current,
    );
    Result:
  • When you're using .reduce, it's important to think about what happens when the array is empty. Otherwise, you may find yourself surprised if you see the error shown above.

  • Reduce is a very abstract method: it can do many different things. The ways to use abstract methods aren't always obvious at first. Once you're comfortable with them, applications will usually show up.

  • However, it's easy to take .reduce too far, so it should be used cautiously. Don't be afraid to use .reduce in simple cases, like summing an array of numbers. If you find yourself struggling to read or write a complex .reduce callback, consider using a loop instead. Saving lines is nice when it helps readability, but six easy lines of loop code are better than one confusing .reduce line!