JavaScript Reduce- Complete Guide with Examples
What is JavaScript Reduce?
The reduce() method is an array method that processes each element and returns a single value. It takes a callback function and an optional initial value.
Most developers encounter it and freeze up. The syntax looks intimidating. The documentation talks about "accumulators" and "reducers" like you're writing a math proof.
It's not that complicated. Reduce is just a loop that carries a running value forward.
The Basic Syntax
array.reduce(callback, initialValue)
The callback receives four arguments:
- accumulator — the value carried from the previous iteration
- currentValue — the current element being processed
- currentIndex — the index of the current element (optional)
- array — the original array (optional)
The Simplest Example First
Sum all numbers in an array:
const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((acc, curr) => acc + curr, 0);
console.log(sum); // 15
Walk through what happens:
- Start: accumulator = 0 (the initial value)
- First iteration: 0 + 1 = 1
- Second iteration: 1 + 2 = 3
- Third iteration: 3 + 3 = 6
- Fourth iteration: 6 + 4 = 10
- Fifth iteration: 10 + 5 = 15
- Return: 15
The initial value matters. Without it, the first iteration uses the first element as the accumulator. This causes bugs when your array is empty or when the first element isn't a valid starting point.
Common Use Cases
Counting Occurrences
const fruits = ['apple', 'banana', 'apple', 'orange', 'banana', 'apple'];
const count = fruits.reduce((acc, fruit) => {
acc[fruit] = (acc[fruit] || 0) + 1;
return acc;
}, {});
console.log(count);
// { apple: 3, banana: 2, orange: 1 }
Grouping Objects by a Property
const people = [
{ name: 'Alice', age: 25 },
{ name: 'Bob', age: 30 },
{ name: 'Charlie', age: 25 }
];
const grouped = people.reduce((acc, person) => {
const key = person.age;
if (!acc[key]) acc[key] = [];
acc[key].push(person);
return acc;
}, {});
console.log(grouped);
// { 25: [Alice, Charlie], 30: [Bob] }
Flattening Nested Arrays
const nested = [[1, 2], [3, 4], [5, 6]];
const flat = nested.reduce((acc, curr) => acc.concat(curr), []);
console.log(flat); // [1, 2, 3, 4, 5, 6]
Finding the Maximum Value
const scores = [72, 95, 84, 61, 99, 78];
const max = scores.reduce((acc, curr) => curr > acc ? curr : acc, scores[0]);
console.log(max); // 99
Or simpler with Math.max and spread:
const max = scores.reduce((acc, curr) => Math.max(acc, curr), -Infinity);
Chaining Reduce with Other Methods
Reduce shines when combined with other array methods. You can transform, filter, and aggregate in a single chain.
const products = [
{ name: 'Laptop', price: 999, category: 'electronics' },
{ name: 'Shirt', price: 29, category: 'clothing' },
{ name: 'Phone', price: 699, category: 'electronics' },
{ name: 'Pants', price: 49, category: 'clothing' }
];
// Get total electronics revenue
const total = products
.filter(p => p.category === 'electronics')
.reduce((acc, curr) => acc + curr.price, 0);
console.log(total); // 1698
Reduce vs Other Array Methods
You don't always need reduce. Sometimes map or filter does the job cleaner.
| Method | Returns | Use When |
|---|---|---|
| map | Array | Transforming each element |
| filter | Array | Selecting elements that match a condition |
| reduce | Single value (any type) | Aggregating, accumulating, or building something new |
If you're returning an array of the same length, use map. If you're returning a subset, use filter. If you're building something that doesn't map 1:1, use reduce.
Common Mistakes
Forgetting the Initial Value
// Wrong - accumulator starts at first element
const sum = [1, 2, 3].reduce((acc, curr) => acc + curr);
// Returns 6, but breaks on empty arrays
// Correct - explicit initial value
const sum = [1, 2, 3].reduce((acc, curr) => acc + curr, 0);
// Returns 6, handles empty arrays gracefully
Not Returning the Accumulator
// Wrong - forgot to return
const doubled = [1, 2, 3].reduce((acc, curr) => {
acc.push(curr * 2);
// Missing: return acc;
});
// Correct - always return the accumulator
const doubled = [1, 2, 3].reduce((acc, curr) => {
acc.push(curr * 2);
return acc;
}, []);
Mutating Objects When You Shouldn't
If you're building an object, create a new one on each iteration unless you intentionally want mutation. Deep cloning inside reduce kills performance on large datasets.
Getting Started: Your First Reduce
Try this exercise. Given an array of transactions:
const transactions = [
{ type: 'credit', amount: 100 },
{ type: 'debit', amount: 50 },
{ type: 'credit', amount: 75 },
{ type: 'debit', amount: 30 }
];
Calculate the balance. Answer:
const balance = transactions.reduce((acc, t) => {
return t.type === 'credit' ? acc + t.amount : acc - t.amount;
}, 0);
console.log(balance); // 95
Start with simple aggregations. Count things. Sum things. Build objects. Once the pattern clicks, you'll find reduce useful in places you used to write for loops.