map, filter, reduce
map, filter and reduce are the three most-used array methods in modern JavaScript. They all take a callback, none of them mutate the array, and together they cover most of what loops are used for: transform, narrow, summarise. Once they click, your code reads more like a description of what you want than a recipe for how to compute it.
map — one in, one out
map(fn) returns a new array where each element has been replaced by the result of calling fn. Same length, same order, different values.
const nums = [1, 2, 3, 4];
const doubled = nums.map(n => n * 2);
// [2, 4, 6, 8]
const users = [
{ id: 1, name: "Ada" },
{ id: 2, name: "Lin" },
];
const names = users.map(u => u.name);
// ["Ada", "Lin"]
// The callback also receives (index, array)
nums.map((n, i) => `#${i}: ${n}`);
// ["#0: 1", "#1: 2", "#2: 3", "#3: 4"]filter — keep the ones you want
filter(predicate) returns a new array containing only the elements for which the callback returns truthy. The result is the same shape but typically shorter.
const nums = [1, 2, 3, 4, 5, 6];
const evens = nums.filter(n => n % 2 === 0);
// [2, 4, 6]
const orders = [
{ id: 1, total: 49, paid: true },
{ id: 2, total: 0, paid: false },
{ id: 3, total: 120, paid: true },
];
orders.filter(o => o.paid && o.total > 0);
// [{ id: 1, ... }, { id: 3, ... }]reduce — fold an array into one value
reduce(fn, initial) walks the array carrying an accumulator. The callback receives (acc, element, index, array) and returns the next accumulator. The final accumulator is the return value.
const nums = [1, 2, 3, 4];
// Sum
const sum = nums.reduce((acc, n) => acc + n, 0); // 10
// Max
const max = nums.reduce((acc, n) => (n > acc ? n : acc), -Infinity);
// Group by key
const people = [
{ name: "Ada", country: "UK" },
{ name: "Lin", country: "TW" },
{ name: "Pedro", country: "BR" },
{ name: "Tina", country: "UK" },
];
const byCountry = people.reduce((acc, p) => {
(acc[p.country] ??= []).push(p);
return acc;
}, {});
// { UK: [Ada, Tina], TW: [Lin], BR: [Pedro] }When to choose which
Same length, transformed values?
map.Shorter (or same) length, same shape?
filter.One summary value (sum, group, max, build object/Map)?
reduce.Map then filter or filter then map? Use
flatMapto do both in one pass.Side effects only? None of these — use a plain loop.
Chaining
Because each method returns a new array, you can chain them into a small pipeline. Read top-to-bottom: each line is a step.
const orders = [
{ id: 1, total: 49, status: "paid" },
{ id: 2, total: 0, status: "pending" },
{ id: 3, total: 120, status: "paid" },
{ id: 4, total: 80, status: "paid" },
];
const totalPaid = orders
.filter(o => o.status === "paid")
.map(o => o.total)
.reduce((a, b) => a + b, 0);
// 49 + 120 + 80 = 249Performance notes
Each method in a chain walks the array once and allocates an intermediate array. For small arrays this cost is invisible. For very large arrays (tens of thousands of elements) on hot paths it can matter:
A 3-method chain over 100k items does 3 walks and 2 intermediate arrays — usually still fine.
If profiling shows it is hot, fuse into a single
reduceorfor...ofloop withpush(one walk, one allocation).Premature fusion costs readability — measure first, then optimise.
Single-pass equivalent
const totalPaid = orders.reduce((sum, o) => {
if (o.status === "paid") sum += o.total;
return sum;
}, 0);Tiny gotcha: parseInt with map
["1", "2", "3"].map(parseInt);
// [1, NaN, NaN] (!)
// Because map calls parseInt(value, index)
// parseInt("2", 1) — radix 1 is invalid → NaN
// Fix: wrap in a one-argument arrow
["1", "2", "3"].map(s => parseInt(s, 10));
// [1, 2, 3]