Topic 5.5
reduce
In one line
reduce walks through an array carrying one running value (the accumulator) and combines each item into it, turning a whole list into a single result such as a total, a count, a maximum or a grouped object; always give it a starting value.
Think of it like this
A piggy bank. You start with an empty piggy bank (the starting value, 0). Each day you drop in that day's coins (each item). The piggy bank carries what's inside from one day to the next (the accumulator). At the end of the month, you have one thing: the total. reduce does that: many items in, one value out.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- reduce
- A method that combines all items of an array into one value, step by step.
- Accumulator
- The running result that
reducecarries from one item to the next. - Initial value
- The starting accumulator, the second argument to
reduce, like0for a sum. - Reducer
- The function you give
reduce: it takes the accumulator and one item and returns the new accumulator. - Group
- Sort items into buckets by some key, for example people by city.
- Object.groupBy
- An ES2024 built-in that groups array items into an object of arrays in one call.
Step by step
01Write the reducer
A reducer takes "the result so far" and "one more item" and returns the new result so far.
const add = (acc, n) => acc + n;
const total = [5, 10, 15].reduce(add, 0);
console.log(total); // 3002Follow the accumulator
Each step's return value becomes the next step's acc.
03Count things
Start with an empty object. Each item adds one to its own counter. ?? 0 gives 0 the first time a key is seen.
const counts = ["a", "b", "a"].reduce((acc, letter) => {
acc[letter] = (acc[letter] ?? 0) + 1;
return acc;
}, {});
// { a: 2, b: 1 }04Group things
Same idea, but each key holds an array. Or let Object.groupBy do it.
const byCity = Object.groupBy(people, (p) => p.city);
// { Pune: [ ... ], Delhi: [ ... ] }05Always pass the initial value
Without it, an empty array throws, and arrays of objects start with an object as the accumulator.
[].reduce((a, b) => a + b); // TypeError
[].reduce((a, b) => a + b, 0); // 0Try it yourself
- 1
Find the longest word
Use
reduceon["hi", "hello", "hey"]to return the longest word. What initial value makes sense? Predict, then run. - 2
Average
In the first example, compute the average of
numsfromtotal. Then predict what happens with an empty array (dividing by zero length). - 3
Forget the return
In the count example, delete
return acc;. Predict the error before you run it, then read the message.
Code & diagrams
The initial value sets the starting point and the type: 0 for a sum, 1 for a product, a string for joining text.
Console
(nothing was printed)Expected console output
acc = 0 n = 5
acc = 5 n = 10
acc = 15 n = 15
total: 30
product: 24
I learn every day`??` is ES2020, `??=` is ES2021 and `Object.groupBy` is ES2024. `Object.groupBy` keeps the whole objects in each group; the hand-written reducer kept only names.
Preview
Console
Nothing logged yet.Expected console output
{ tea: 3, coffee: 2, juice: 1 }
{ Pune: [ 'Asha', 'Meera' ], Delhi: [ 'Ravi' ] }
[ 'Pune', 'Delhi' ] 2Without an initial value, the first object became the accumulator and `+` turned it into the text `[object Object]`. The last line throws on purpose.
Console
(nothing was printed)Expected console output
6
0
9
[object Object]20
30
Uncaught TypeError: Reduce of empty array with no initial valueBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
No initial value on an array that can be empty
Compute a cart total with cart.reduce((sum, n) => sum + n) when the cart is empty.
Break #2
Forgetting to return the accumulator
Write the counting reducer as (acc, v) => { acc[v] = (acc[v] ?? 0) + 1; } without return acc;.
Myth vs fact
Myth
reduce is only for adding numbers.
Fact
The accumulator can be any type: objects for counts and groups, arrays, strings, even a promise chain. The initial value picks the type.
Myth
The initial value is optional, so you can skip it.
Fact
It's technically optional, but skipping it breaks on empty arrays and on arrays of objects. Always pass it unless you're sure the array is non-empty and the items are the same type as the result.
Myth
Using reduce is always the most advanced, best way.
Fact
A reducer full of branches is often harder to read than a for...of loop. Pick the clearest tool; named methods like Object.groupBy or Math.max(...arr) say more.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Building a new object every step (
(acc, x) => ({ ...acc, [x.id]: x })) copies the whole accumulator each time, making the reduce O(n²). Mutating the accumulator you created yourself (acc[x.id] = x; return acc;) is O(n) and still pure from the outside. - ▸
Object.groupByreturns an object with a null prototype (no inheritedtoStringand friends), so keys like"constructor"are safe.Map.groupByreturns aMap, which lets keys be objects or any type. - ▸
reduceskips holes. Without an initial value, the first present element is used, so[, , 5].reduce(f)returns5without callingfat all. - ▸
reduceis the same idea as fold in other languages. Interview tasks like "implementmapwithreduce" test that you understand the accumulator:arr.reduce((acc, x) => { acc.push(f(x)); return acc; }, []).
Remember this
- 1
arr.reduce(fn, initialValue)callsfn(accumulator, element, index, array)for each element. The accumulator (often calledacc) is the running result: on the first call it isinitialValue; on every later call it is whatever your function returned last time. After the last element,reducereturns the final accumulator. The classic example is a sum:[5, 10, 15].reduce((acc, n) => acc + n, 0)goes 0 → 5 → 15 → 30. - 2
The result can be any type: a number (a total), a string, an object (counts per word, items grouped by city), even another array. The initial value decides the type, so choose it on purpose:
0for sums,1for products,{}for counting or grouping,[]for building a list. - 3
Initial value pitfalls. If you leave out
initialValue,reduceuses the first element as the starting accumulator and begins at the second one. That works for summing plain numbers, but goes wrong in two ways: on an empty array it throwsTypeError: Reduce of empty array with no initial value, and when the items are objects, the first accumulator is an object, soacc + item.priceproduces text like"[object Object]20". Rule: always pass the initial value. - 4
Counting with
reduce: start with{}, and for each item increaseacc[item]by one:acc[item] = (acc[item] ?? 0) + 1; return acc;. Grouping: start with{}, and push each item into the list for its key. Since ES2024,Object.groupBy(arr, fn)andMap.groupBy(arr, fn)do grouping for you in one call and are clearer when available. - 5
Your callback must return the accumulator every time. Forgetting the
return(easy with{ }arrow bodies) makes the accumulatorundefinedon the next step.reduceRightdoes the same asreducebut walks from the last element to the first. - 6
reduceis powerful, but not always the clearest tool. A sum or a count reads well asreduce. A long reducer withifs and several fields is often clearer as a plainfor...ofloop. Many tasks have a named method that says more:Math.max(...arr)for a maximum,filter(...).lengthfor counting matches,Object.groupByfor grouping,flat()for flattening.
Explain it without notes
Explain how reduce works step by step for a sum.
What happens when you call reduce without an initial value?
How would you count how many times each word appears in an array?
When is a for...of loop better than reduce?
Practice
Use reduce to find the total price of [{ name: "Pen", price: 20 }, { name: "Bag", price: 900 }].
Use reduce to turn [["a", 1], ["b", 2]] into { a: 1, b: 2 }.
Group [1, 2, 3, 4, 5] into { odd: [...], even: [...] } with Object.groupBy.
Trade-offs
- ↔
reduceexpresses "many into one" in a single expression; a loop is often easier to read and debug when the logic grows. - ↔
Object.groupByis the clearest way to group, but needs a 2024-or-later browser; a hand-written reducer works everywhere.
Done when you can
I can explain what the accumulator is and how it changes.
I can sum, multiply and join with
reduceand the right initial value.I can count and group items into an object.
I always pass an initial value and always return the accumulator.
I can choose between
reduce, a loop andObject.groupBy.