reduce() is the most capable and most underused JavaScript array method. Most developers know it computes sums. Fewer realize it handles grouping, indexing, deduplication, and building any data structure from an array. This guide walks through the practical applications step by step.
Step 1: Understand the Accumulator
reduce() takes a callback and an optional initial value. The callback receives the accumulator (the running result) and the current element. Whatever the callback returns becomes the new accumulator.
array.reduce((accumulator, current) => {
// return the new accumulator
}, initialValue);
The initial value determines what kind of result you get. Start with 0 to sum numbers. Start with {} to build an object. Start with [] to build an array. The type of the initial value is the type of the final result.
Step 2: Sum and Average
The basic case: summing an array of numbers.
const prices = [12.99, 5.50, 22.00, 8.75];
const total = prices.reduce((sum, price) => sum + price, 0);
// 49.24
// Average:
const avg = prices.reduce((sum, p, i, arr) => {
sum += p;
return i === arr.length - 1 ? sum / arr.length : sum;
}, 0);
// 12.31
The callback receives four arguments: accumulator, current value, current index, and the original array. The index and array are available when you need them (for the average calculation above, the index tells you when you are on the last element).
Step 3: Group Items by a Property
This is where reduce() pulls ahead of alternatives. Group an array of objects by one of their fields.
const transactions = [
{ id: 1, category: "food", amount: 15 },
{ id: 2, category: "transport", amount: 8 },
{ id: 3, category: "food", amount: 22 },
{ id: 4, category: "entertainment", amount: 45 },
{ id: 5, category: "transport", amount: 12 }
];
const byCategory = transactions.reduce((groups, tx) => {
const key = tx.category;
if (!groups[key]) groups[key] = [];
groups[key].push(tx);
return groups;
}, {});
/*
{
food: [{ id: 1, ... }, { id: 3, ... }],
transport: [{ id: 2, ... }, { id: 5, ... }],
entertainment: [{ id: 4, ... }]
}
*/
The initial value is {}. Each iteration checks whether the group key exists, creates it if not, and pushes the current item. The result is a grouped object you can access by category name.
A common extension: sum by category at the same time.
const totalByCategory = transactions.reduce((totals, tx) => {
totals[tx.category] = (totals[tx.category] || 0) + tx.amount;
return totals;
}, {});
// { food: 37, transport: 20, entertainment: 45 }
Step 4: Build a Lookup Object
When you need to look items up by ID repeatedly, building a lookup object from the array once is more efficient than calling find() each time.
const products = [
{ id: "abc", name: "Widget", price: 9.99 },
{ id: "def", name: "Gadget", price: 24.99 },
{ id: "ghi", name: "Doohickey", price: 4.99 }
];
const productsById = products.reduce((map, p) => {
map[p.id] = p;
return map;
}, {});
// O(1) access:
const product = productsById["def"];
Build this map once when the data arrives (from an API, for example) and use it throughout the lifecycle of the page or request. find() is fine for occasional lookups; this pattern is for repeated lookups.
Step 5: Flatten and Transform in One Pass
Instead of chaining filter() and map() (two passes), use reduce() to filter and transform in a single pass.
const orders = [
{ id: 1, status: "completed", total: 49.00 },
{ id: 2, status: "pending", total: 22.00 },
{ id: 3, status: "completed", total: 78.50 }
];
const completedTotals = orders.reduce((acc, order) => {
if (order.status === "completed") {
acc.push(order.total);
}
return acc;
}, []);
// [49.00, 78.50]
The initial value is [], so the accumulator is an array. Each matching item is pushed; non-matching items are skipped. The result is both filtered and extracted in one iteration.
Step 6: Count Occurrences
Counting how many times each value appears is a common aggregation task.
const responses = ["yes", "no", "yes", "maybe", "yes", "no"];
const counts = responses.reduce((acc, r) => {
acc[r] = (acc[r] || 0) + 1;
return acc;
}, {});
// { yes: 3, no: 2, maybe: 1 }
Step 7: Deduplicate with reduce()
For deduplicating an array of objects by a unique field, reduce() with a lookup flag is one of the cleaner approaches.
const seen = {};
const unique = items.reduce((acc, item) => {
if (!seen[item.id]) {
seen[item.id] = true;
acc.push(item);
}
return acc;
}, []);
This preserves the first occurrence of each id and ignores duplicates.
Step 8: Compute Running Totals or Cumulative Values
reduce() can build arrays where each element depends on the previous one, something map() cannot do because map() treats each element independently.
const dailySales = [120, 85, 200, 150, 90];
const cumulativeSales = dailySales.reduce((acc, sale) => {
const runningTotal = (acc[acc.length - 1] || 0) + sale;
acc.push(runningTotal);
return acc;
}, []);
// [120, 205, 405, 555, 645]
Each element in the result is the sum of all sales up to and including that day. The accumulator is an array, and each iteration reads the last element to compute the next.
This pattern applies to any situation where the output value depends on what came before: running maximum, running average, percentage-of-total, or any sequential computation.
Step 9: Validate and Summarize in One Pass
When you need to both check validity and collect results, reduce() handles both in a single pass.
const inputs = [
{ value: 42, valid: true },
{ value: -5, valid: false },
{ value: 100, valid: true }
];
const summary = inputs.reduce((acc, input) => {
if (!input.valid) {
acc.errors += 1;
} else {
acc.values.push(input.value);
acc.total += input.value;
}
return acc;
}, { values: [], total: 0, errors: 0 });
// { values: [42, 100], total: 142, errors: 1 }
The accumulator is an object with multiple fields. Each iteration updates multiple fields based on the current item. The result is a complete summary without multiple passes over the input data.
Where to Go From Here
reduce() handles all of these cases because it is a general-purpose accumulation primitive. The specific array methods (filter(), map(), some()) are specialized versions of what reduce() can do, and they are more readable for their specific cases. Use filter() and map() when they clearly express the intent. Reach for reduce() when you need grouping, aggregation, or multi-step transformations in a single pass.
The full JavaScript array method reference with snippets for all methods is at 137Foundry in the guide on JavaScript array methods code snippets.
The MDN documentation on Array.prototype.reduce() covers the full method signature, edge cases, and browser compatibility. The Wikipedia article on functional programming provides the theoretical background for the reduce() pattern and how it relates to the fold operation in functional languages. The OWASP Cheat Sheet Series is relevant if you are processing user-supplied data in your pipeline and need to think about input validation alongside transformation. The Wikipedia article on higher-order functions is also useful context for understanding why reduce() is so general-purpose compared to the more specialized array methods.
Top comments (0)