Array.reduce() in JavaScript: folding an array down to one value

reduce walks an array while carrying an accumulator: the initial value, grouping data, and the cases where a plain loop reads better.
3 min read
Believemy logo

Adding amounts up, counting occurrences, grouping rows by country: three different operations sharing one shape, an array going in and a single value coming out.

reduce is the method describing that shape, and the most feared of the family because it asks you to name two things instead of one.


Definition

reduce walks an Array while carrying one value from pass to pass, the accumulator. On each item the function receives that accumulator and the item, and returns the new accumulator. The last one returned is the result.

JAVASCRIPT
const amounts = [12, 30, 8];

const total = amounts.reduce((sum, a) => sum + a, 0);
console.log(total);   // 50

The trailing 0 is the accumulator's starting value, often taken for a detail. It actually decides the type of the result: 0 for a number, "" for a string, {} for an object, [] for an array.


The initial value is not optional

It can be left out, and the first item then serves as the accumulator. On an empty array, there is nothing left to take.

JAVASCRIPT
try {
  [].reduce((a, b) => a + b);
} catch (err) {
  console.log(err.name);      // TypeError
  console.log(err.message);   // Reduce of empty array with no initial value
}

console.log([].reduce((a, b) => a + b, 0));   // 0

An empty array is no rare case: it is what a Array.filter() hands back when it found nothing, or a query with no results. Writing the initial value costs three characters and removes the failure.


Grouping data

This is where reduce clearly beats a loop, because the output structure is declared at the end of the line.

JAVASCRIPT
const sales = [
  { country: "FR", amount: 40 },
  { country: "BE", amount: 15 },
  { country: "FR", amount: 60 },
];

const byCountry = sales.reduce((acc, s) => {
  acc[s.country] = (acc[s.country] ?? 0) + s.amount;
  return acc;
}, {});

console.log(byCountry);   // { FR: 100, BE: 15 }
Warning

Do not rebuild the accumulator on every pass with Spread (...), as in reduce((acc, s) => ({ ...acc, [s.country]: s.amount }), {}). The cost becomes proportional to the square of the item count, and the line hardly reads better. Change the accumulator, then return it.


Frequently asked questions

Question

When is a plain loop the better choice?

As soon as the function runs past five or six lines, or has to stop early. reduce always walks the whole array, with no way out. A for loop with a break then says the same thing far more plainly.

Question

Can reduce replace Array.map() and filter?

Technically yes, in a single pass. In practice the speed gain is small and the code reads markedly worse: the name map announces a transformation, a reduce announces nothing. Keep it for what the others cannot do.

Question

What is reduceRight for?

For walking the array from the end to the start, with the same signature. The real case is function composition, where the order of application matters. For a sum or a count, the direction changes nothing in the result.

Related terms

Discover our javaScript glossary

Every word of JavaScript explained simply: keywords, built-in objects, methods, errors and concepts. Clear definitions and examples that actually run, to learn and to troubleshoot.

Share this article

Want to help us? Share this article on your networks or even better: on your site, in an article or in your newsletter.