Master JavaScript reduce() method: accumulator pattern, sum/count/group/flatten, reduceRight, common mistakes, no initialValue pitfall, real-world use cases, interview Q&A.
reduce() is the most powerful array method. It collapses an array into a single value — that value can be a number, string, object, array, or anything you want. Think of it as folding the array, step by step, into one final result.
Visual: How reduce() Works
Array: [1, 2, 3, 4, 5] initialValue = 0
Step 1: acc=0, curr=1 → 0 + 1 = 1
Step 2: acc=1, curr=2 → 1 + 2 = 3
Step 3: acc=3, curr=3 → 3 + 3 = 6
Step 4: acc=6, curr=4 → 6 + 4 = 10
Step 5: acc=10, curr=5 → 10 + 5 = 15
Result: 15
Step-by-Step Trace
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr, index) => {
console.log(`Step ${index}: acc=${acc}, curr=${curr}, → ${acc + curr}`);
return acc + curr;
}, 0);
console.log("Final:", sum);
Step 0: acc=0, curr=1, → 1
Step 1: acc=1, curr=2, → 3
Step 2: acc=3, curr=3, → 6
Step 3: acc=6, curr=4, → 10
Step 4: acc=10, curr=5, → 15
Final: 15
Basic Patterns
Sum and Product
const nums = [1, 2, 3, 4, 5];
const sum = nums.reduce((acc, n) => acc + n, 0);
const product = nums.reduce((acc, n) => acc * n, 1);
const max = nums.reduce((m, n) => n > m ? n : m, -Infinity);
const min = nums.reduce((m, n) => n < m ? n : m, Infinity);
console.log("Sum:", sum);
console.log("Product:", product);
console.log("Max:", max);
console.log("Min:", min);
Sum: 15
Product: 120
Max: 5
Min: 1
const cart = [
{ item: "Laptop", price: 80000, qty: 1 },
{ item: "Mouse", price: 500, qty: 2 },
{ item: "Keyboard", price: 1500, qty: 1 }
];
const total = cart.reduce((sum, product) => sum + product.price * product.qty, 0);
console.log("Cart Total: ₹" + total.toLocaleString());
Reducing to Objects
Count Occurrences
const fruits = ["apple", "banana", "apple", "cherry", "banana", "apple"];
const freq = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(freq);
{apple: 3, banana: 2, cherry: 1}Group By Property
const people = [
{ name: "Alice", dept: "Engineering" },
{ name: "Bob", dept: "Marketing" },
{ name: "Charlie", dept: "Engineering" },
{ name: "Diana", dept: "Marketing" }
];
const grouped = people.reduce((groups, person) => {
const key = person.dept;
(groups[key] = groups[key] || []).push(person.name);
return groups;
}, {});
console.log(grouped);
{Engineering: ["Alice", "Charlie"], Marketing: ["Bob", "Diana"]}
Reducing to Arrays
Flatten Nested Arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, arr) => [...acc, ...arr], []);
console.log(flat);
// Deep flatten
const deepNested = [1, [2, [3, [4]]]];
function deepFlatten(arr) {
return arr.reduce((acc, item) =>
Array.isArray(item) ? [...acc, ...deepFlatten(item)] : [...acc, item],
[]);
}
console.log(deepFlatten(deepNested));
[1, 2, 3, 4, 5, 6]
[1, 2, 3, 4]
Remove Duplicates
const arr = [1, 2, 2, 3, 3, 3, 4];
const unique = arr.reduce((acc, n) => {
if (!acc.includes(n)) acc.push(n);
return acc;
}, []);
console.log(unique);
Implementing map() and filter() with reduce()
// map using reduce
function myMap(arr, fn) {
return arr.reduce((acc, item) => [...acc, fn(item)], []);
}
console.log(myMap([1, 2, 3], n => n * 2));
// filter using reduce
function myFilter(arr, fn) {
return arr.reduce((acc, item) => fn(item) ? [...acc, item] : acc, []);
}
console.log(myFilter([1, 2, 3, 4, 5], n => n % 2 === 0));
The initialValue Trap
// ✅ With initialValue — safe
const a = [1, 2, 3];
console.log(a.reduce((acc, n) => acc + n, 0)); // 6
// ⚠️ Without initialValue — first element becomes acc (skip step 0)
console.log(a.reduce((acc, n) => acc + n)); // 6 — same here
// ❌ Without initialValue on empty array — TypeError!
try {
[].reduce((acc, n) => acc + n);
} catch (e) {
console.log("Error:", e.message);
}
// ✅ With initialValue on empty array — safe
console.log([].reduce((acc, n) => acc + n, 0)); // 0
6
6
Error: Reduce of empty array with no initial value
0
reduceRight() — Right to Left
const arr = [1, 2, 3, 4];
// Normal reduce (left to right)
const leftResult = arr.reduce((acc, n) => `(${acc}+${n})`);
console.log(leftResult);
// reduceRight (right to left)
const rightResult = arr.reduceRight((acc, n) => `(${acc}+${n})`);
console.log(rightResult);
(((1+2)+3)+4)
(((4+3)+2)+1)
Real World Use Cases
// 1. Pipeline / compose functions
const pipe = (...fns) => input => fns.reduce((v, fn) => fn(v), input);
const processPrice = pipe(
price => price * 1.18, // add 18% GST
price => price.toFixed(2), // format
price => `₹${price}` // add symbol
);
console.log(processPrice(1000));
// 2. Build a lookup map (O(1) search)
const users = [
{ id: 1, name: "Alice" },
{ id: 2, name: "Bob" },
{ id: 3, name: "Carol" }
];
const userMap = users.reduce((map, user) => {
map[user.id] = user;
return map;
}, {});
console.log(userMap[2].name); // Direct O(1) lookup
// 3. Sum by category
const sales = [
{ category: "Electronics", amount: 5000 },
{ category: "Clothing", amount: 1200 },
{ category: "Electronics", amount: 3000 },
{ category: "Clothing", amount: 800 }
];
const byCategory = sales.reduce((acc, sale) => {
acc[sale.category] = (acc[sale.category] || 0) + sale.amount;
return acc;
}, {});
console.log(byCategory);
₹1180.00
Bob
{Electronics: 8000, Clothing: 2000}
Common Mistakes
| ❌ Wrong | ✅ Correct | Why |
|---|
arr.reduce(fn) on empty array | arr.reduce(fn, 0) | Without initialValue, empty array throws TypeError |
Forgetting to return acc | Always return acc at the end | Without return, acc becomes undefined on next step |
Using reduce() for simple sums | Simple sum is fine, but complex reduce = harder to read | Write a comment explaining what the accumulator represents |
Mutating acc object: acc.key = val; return acc | This works but be intentional | Works because objects are references, but avoid bugs by being explicit |
Cheat Sheet
Sum: arr.reduce((acc, n) => acc + n, 0)
Max: arr.reduce((m, n) => n > m ? n : m, -Infinity)
Count: arr.reduce((acc, v) => ({...acc, [v]: (acc[v]||0)+1}), {})
Flatten: arr.reduce((acc, a) => [...acc, ...a], [])
Group by: arr.reduce((acc, v) => ({...acc, [v.key]: [...(acc[v.key]||[]), v]}), {})
Unique: arr.reduce((acc, v) => acc.includes(v) ? acc : [...acc, v], [])
Map (custom): arr.reduce((acc, v) => [...acc, fn(v)], [])
Lookup map: arr.reduce((acc, v) => ({...acc, [v.id]: v}), {})
Interview Questions
Q1. What does reduce() return?
A single accumulated value — can be a number, string, array, object, or any type depending on how you write the reducer and initialValue.
Q2. What is the initialValue and why is it important?
initialValue is the starting value of the accumulator. Without it, reduce() uses the first element as the accumulator and starts from index 1. On an empty array, calling reduce() without initialValue throws a TypeError. Always provide an initialValue.
Q3. Does reduce() mutate the original array?
No. reduce() is a non-mutating method. It reads each element but doesn't change the array.
Q4. How is reduce() different from map() and filter()?
map() and filter() always return arrays. reduce() can return any type — it collapses the array into one value. You can actually implement both map() and filter() using reduce().
Q5. What happens if you don't return acc in the reducer?
The accumulator becomes undefined on the next call. This is a very common bug — always ensure your reducer returns the accumulator.
Q6. What is reduceRight()?
Same as reduce() but iterates from right to left (last element to first). Useful for right-to-left composition and reversing accumulation order.
Q7. How would you use reduce() to implement a function pipeline?
const pipe = (...fns) => x => fns.reduce((v, fn) => fn(v), x);
const process = pipe(n => n * 2, n => n + 1, n => n ** 2);
console.log(process(3)); // ((3*2)+1)^2 = 49
Key Takeaways
reduce() collapses an array into any single value — most versatile array method- Always provide
initialValue — protects against TypeError on empty arrays - The reducer must always return the accumulator — forgetting this is the #1 bug
reduce() can implement map(), filter(), flat(), and many more- Use it for: sums, counts, grouping, flattening, pipelines, lookup maps
reduceRight() processes right-to-left