Array.prototype.reduce()
Folds an array left to right into a single accumulated value.
reduce(callbackFn)
reduce(callbackFn, initialValue)- Added in JavaScript ES5 (2009)
Parameters
| Parameter | Type | Description |
|---|---|---|
$callbackFn | Function | The reducer, called with (accumulator, currentValue, currentIndex, array). Its return value becomes the accumulator for the next call. |
$initialValueoptional | any= array[0] | Starting value for the accumulator. If omitted, array[0] is used as the accumulator and iteration begins at index 1. |
Return value
anyThe single value produced by running the reducer over every element; if the array has one element and no initialValue, that element is returned without calling the callback.
Errors
TypeError if the array contains no elements and no initialValue was supplied.
reduce() collapses many values into one. The callback receives the running accumulator and the current element, and whatever it returns becomes the accumulator for the next element. The accumulator does not have to be a number — objects, arrays, Maps and even functions are all common.
Always pass initialValue
Omitting the second argument makes reduce() use array[0] as the seed and start at index 1. On an empty array there is no element 0 to borrow, so it throws TypeError: Reduce of empty array with no initial value. Since empty arrays are usually the runtime case you did not test, supply the seed: reduce(fn, 0) or reduce(fn, {}). The seed also pins the accumulator's type, which stops sums from turning into string concatenation.
What trips people up
Every branch of the callback must return the accumulator. Writing acc[key] = value without a trailing return acc hands undefined to the next iteration and everything after it fails.
Reaching for reduce() when map() or filter() would do makes code harder to read for no gain. Save it for genuine folds: totals, groupings and single-pass pipelines.
Example
When to use it
- Total the line items in a shopping cart
- Group records into an object keyed by category
- Count occurrences of each value into a tally object
- Compose a pipeline of transform functions into one function
Frequently asked questions
Why does reduce() throw 'Reduce of empty array with no initial value'?
Because with no initialValue reduce() takes array[0] as the seed, and an empty array has no element 0. Pass an explicit initial value — reduce(fn, 0) returns 0 for an empty array instead of throwing.
What is the difference between reduce() and forEach()?
reduce() threads an accumulator through the callback and returns a value; forEach() returns undefined and relies on side effects. If you are pushing into an outer variable inside forEach, reduce() usually expresses the same thing without the external state.
Is reduce() slower than a for loop?
Marginally, because of the per-element function call, and it is much slower if the callback copies the accumulator each pass, such as returning { ...acc, [k]: v }. That turns an O(n) fold into O(n²) — mutate the accumulator and return it instead.
More examples
Grouping records by a key
Prints { eng: ['Ada', 'Ray'], ops: ['Lin'] }. The seed {} makes the accumulator an object rather than the first record, and the explicit return acc on the last line of the callback is what keeps the accumulator alive across iterations.
const people = [
{ name: "Ada", dept: "eng" },
{ name: "Lin", dept: "ops" },
{ name: "Ray", dept: "eng" },
];
const byDept = people.reduce((acc, p) => {
(acc[p.dept] ??= []).push(p.name);
return acc;
}, {});
console.log(byDept);Building a pipe() out of reduce
Prints 64. The accumulator here is the data flowing through the pipeline, not a total: 3 becomes 4, then 8, then 64. This is the left-to-right order; reduceRight() gives you compose() with the same body.
const inc = (n) => n + 1;
const dbl = (n) => n * 2;
const sq = (n) => n * n;
const pipe = (...fns) => (x) => fns.reduce((value, fn) => fn(value), x);
console.log(pipe(inc, dbl, sq)(3));
Discussion