Array and object methods that matter: map, filter, reduce, destructuring, spread
These aren't just shorter syntax for a for-loop — map, filter, and reduce each encode a specific INTENT (transform, select, combine), and reading code written in them tells you what's happening before you've even read the callback body.
4 min read
map: transform every element, same length in and out
const prices = [10, 20, 30];
const withTax = prices.map((price) => price * 1.08); // [10.8, 21.6, 32.4] — SAME length as pricesmap promises exactly one thing: the output array has the same length as the input, with each element independently transformed by the callback. Seeing .map() in code is an immediate, reliable signal — even before reading the callback — that this is a one-to-one transformation, nothing being added, removed, or combined. A for loop achieving the same result carries no such built-in guarantee; the intent has to be inferred from reading the whole loop body.
filter: select a subset, same or shorter, never longer
const users = [{ age: 17 }, { age: 25 }, { age: 15 }, { age: 30 }];
const adults = users.filter((user) => user.age >= 18); // only the elements where the callback returned truefilter promises the opposite guarantee from map: the output is always a subset of the input (same elements, possibly fewer, never more, never transformed) — every element for which the callback returned a truthy value survives, in the original order. The intent signaled is "keep some, discard the rest," distinct from map's "transform every one."
reduce: combine everything into a single result, of any shape
const cart = [{ price: 10 }, { price: 25 }, { price: 15 }];
const total = cart.reduce((sum, item) => sum + item.price, 0); // 50 — a single NUMBER, not an array
const byId = cart.reduce((acc, item, i) => {
acc[i] = item; // building an OBJECT this time — reduce's result can be any shape at all
return acc;
}, {});reduce is the most general of the three — instead of a fixed output shape (same-length array, or a subset), it builds up a single accumulated result across every element, and that result can be a number, an object, a string, another array, or anything else entirely. This generality is also why reduce is the hardest of the three to read at a glance: map and filter signal their intent immediately by name, while a reduce call requires actually reading the callback to know what's being built — worth keeping in mind when a simpler map or filter would communicate the same intent more directly.
Destructuring: pulling values out of arrays/objects by position or name
const [first, second] = [10, 20, 30]; // first=10, second=20 — 30 is simply not captured
const { name, age = 0 } = { name: "Ada" }; // name="Ada", age=0 — DEFAULT applies since age isn't present
function greet({ name, role = "guest" }) { // destructuring directly in a parameter list
return `${name} (${role})`;
}
greet({ name: "Ada" }); // "Ada (guest)"Array destructuring pulls values by position; object destructuring pulls values by matching property name — genuinely different mechanisms, even though the syntax looks parallel. Both support default values (used exactly when the corresponding value is undefined, the same rule as default parameters), and both work directly in a function's parameter list, which is an extremely common real pattern for functions that take a single "options" object — the destructuring documents which properties the function actually cares about, directly in the signature.
Spread: expanding an iterable into individual elements
const a = [1, 2];
const b = [3, 4];
const combined = [...a, ...b, 5]; // [1, 2, 3, 4, 5] — each array's elements spread individually
const original = { name: "Ada", role: "engineer" };
const updated = { ...original, role: "lead" }; // a NEW object — role overwritten, name kept, ORIGINAL untouchedSpread (...) expands an array or object's contents into a new array or object literal — critically, it produces a genuinely new array/object rather than mutating the original, which is exactly the pattern React's state-update rules (covered in this platform's React domain) depend on: { ...original, role: "lead" } creates a fresh object with the shallow copy of everything from original, then overwrites role, leaving original itself completely unchanged.
Further reading
Check your understanding
A quick comprehension check — not tracked, not graded, just for you.
1. What guarantee does `.map()` make about its output that a plain for-loop doesn't signal on its own?
2. Why is `reduce()` considered harder to read at a glance than `map()` or `filter()`?
3. Why does spread (`{ ...original, role: 'lead' }`) matter specifically for React-style state updates?