Mastering JavaScript's reduce() Method and Advanced Patterns
The reduce() method processes array elements to produce a single accumulated result. While tasks achievable with reduce() can often be implemented using for loops or forEach(), reduce() offers a more declarative, functional approach that can simplify complex aggregations and transformations.
Syntax
arr.reduce(callback, initialValue)
The callback receives up to four arguments:
accumulator: the accumulated value from the previous iteration (orinitialValueon the first call)currentValue: the current element being proecssedcurrentIndex: the index of the current elementarray: the original array
The optional initialValue serves as the starting point for the accumulator.
Behavior with and without Initial Value
When no initialValue is provided, reduce() starts at index 1, using the first element as the initial accumulator:
const values = [1, 2, 3, 4];
const total = values.reduce((acc, val, idx) => {
console.log(acc, val, idx);
return acc + val;
});
// Logs:
// 1 2 1
// 3 3 2
// 6 4 3
// Result: 10
With an explicit initialValue, iteration begins at index 0:
const totalWithInit = values.reduce((acc, val, idx) => {
console.log(acc, val, idx);
return acc + val;
}, 0);
// Logs:
// 0 1 0
// 1 2 1
// 3 3 2
// 6 4 3
// Result: 10
If the array is empty and no initialValue is given, reduce() throws a TypeError. Providing an initial value avoids this error and returns the initial value directly.
Common Use Cases
Summation and product:
const nums = [1, 2, 3, 4];
const sum = nums.reduce((a, b) => a + b); // 10
const product = nums.reduce((a, b) => a * b); // 24
Counting occurrences:
const names = ['Alice', 'Bob', 'Tiff', 'Bruce', 'Alice'];
const counts = names.reduce((tally, name) => {
tally[name] = (tally[name] || 0) + 1;
return tally;
}, {});
// { Alice: 2, Bob: 1, Tiff: 1, Bruce: 1 }
Array deduplication:
const duplicates = [1, 2, 3, 4, 4, 1];
const unique = duplicates.reduce((acc, item) => {
return acc.includes(item) ? acc : [...acc, item];
}, []);
// [1, 2, 3, 4]
Flattening arrays:
// 2D to 1D
const nested = [[0, 1], [2, 3], [4, 5]];
const flat = nested.reduce((acc, sub) => acc.concat(sub), []);
// [0, 1, 2, 3, 4, 5]
// Recursive flattening for arbitrary depth
const deepNested = [[0, 1], [2, 3], [4, [5, 6, 7]]];
const flatten = arr => arr.reduce(
(acc, val) => acc.concat(Array.isArray(val) ? flatten(val) : val),
[]
);
// [0, 1, 2, 3, 4, 5, 6, 7]
Summing object properties:
const scores = [
{ subject: 'math', score: 10 },
{ subject: 'chinese', score: 20 },
{ subject: 'english', score: 30 }
];
const totalScore = scores.reduce((sum, entry) => sum + entry.score, 0);
// 60