A for loop can do anything, which is exactly the problem — the reader has to read the whole loop body to figure out what it's doing. Array methods name the operation up front.
Transforming: map
const prices = [10, 20, 30];
const withTax = prices.map((p) => p * 1.08);Reads as "transform each price" — no need to track an index or an accumulator array.
Selecting: filter
const users = [{ active: true }, { active: false }, { active: true }];
const activeUsers = users.filter((u) => u.active);Combining into one value: reduce
const cart = [{ price: 10 }, { price: 25 }, { price: 15 }];
const total = cart.reduce((sum, item) => sum + item.price, 0);reduce is the one people avoid, usually because the signature is unintuitive at first. The mental model: the second argument (0 here) is the starting value, and the callback runs once per item, returning the "running total" each time.
Finding one item: find
const user = users.find((u) => u.id === targetId);
// undefined if not found — no need to pre-check with a loopChecking conditions: some and every
const hasActiveUser = users.some((u) => u.active); // true if ANY match
const allActive = users.every((u) => u.active); // true if ALL matchBoth short-circuit — some stops at the first match, every stops at the first non-match — so they're not slower than a hand-written loop with an early break.
When a plain loop is still the right call
- You need to
breakorcontinuebased on complex conditions mid-iteration (array methods don't support this cleanly). - You're mutating the array you're iterating over (risky with array methods, risky in general).
- Performance-critical hot paths processing very large arrays — a
forloop avoids the per-callback function-call overhead, though this rarely matters outside of tight inner loops in performance-sensitive code.
Chaining — where it helps and where it hurts
const total = cart
.filter((item) => item.inStock)
.map((item) => item.price * item.quantity)
.reduce((sum, price) => sum + price, 0);This reads left-to-right as a pipeline: filter, then transform, then combine. That's a genuine readability win over the equivalent loop. But chaining four or five methods on a large array means four or five full passes over the data — if performance matters and the array is large, a single reduce (or a plain loop) that does the filtering and transforming in one pass is worth the readability tradeoff.