Articles

Map, Filter, Reduce: JavaScript's Core Array Methods

map, filter, and reduce transform arrays without loops or mutation. How each one works, when to reach for it, and where they trip people up.

Takina Takina · · 4 min read
Code editor showing JavaScript array operations

map, filter, and reduce are the three array methods that replace most hand-written for loops in modern JavaScript. Each takes a callback function and returns a new value without mutating the original array: map transforms every element, filter keeps only the elements that pass a test, and reduce folds the whole array down into a single result. Together they cover the vast majority of everyday array processing.

map: transform every element

Array.prototype.map() calls a function on each element and returns a new array of the results, same length as the original.

const prices = [10, 20, 30];
const withTax = prices.map((price) => price * 1.08);
// [10.8, 21.6, 32.4]

The original prices array is untouched. This matters in frameworks like React, where immutable updates keep rendering predictable — mutating an array in place can cause state to go stale or trigger stale re-renders. map is also the standard way to turn data into JSX or template output, one element at a time.

filter: keep what matches

Array.prototype.filter() calls a predicate function on each element and returns a new array containing only the elements where that function returned true.

const users = [{ name: "Ana", active: true }, { name: "Bo", active: false }];
const activeUsers = users.filter((u) => u.active);
// [{ name: "Ana", active: true }]

Like map, the result is always a new array — possibly shorter, never longer. A common mistake is confusing filter with find: filter always returns an array (even if empty or with one match), while find returns the first matching element itself, or undefined.

reduce: fold into one value

Array.prototype.reduce() is the most general of the three, and the one people find least intuitive at first. It walks the array left to right, carrying an accumulator forward:

const total = [10, 20, 30].reduce((acc, price) => acc + price, 0);
// 60

The second argument to reduce is the initial value of the accumulator — always pass it explicitly. Without it, reduce uses the first array element as the seed and starts iterating from the second, which silently misbehaves on empty arrays and produces confusing type errors when the accumulator shape differs from the element shape.

reduce can build more than numbers. Grouping an array into an object, flattening nested arrays, or building a lookup table by ID are all reduce in disguise:

const byId = users.reduce((acc, u) => {
  acc[u.id] = u;
  return acc;
}, {});

That said, map and filter (and, since recent engines, Array.prototype.group()-style helpers) are usually easier to read for their specific jobs. Reach for reduce when you genuinely need to collapse an array into something that isn’t itself an array of the same shape — a sum, a single object, a boolean.

Chaining and performance

These methods compose naturally:

const total = orders
  .filter((o) => o.status === "paid")
  .map((o) => o.amount)
  .reduce((sum, amount) => sum + amount, 0);

Each link in that chain allocates a new intermediate array. For small-to-medium arrays this is irrelevant, and the readability win is worth far more than the allocation cost. For very large arrays in a hot path, a single for loop or one combined reduce avoids the intermediate arrays — but profile before optimizing. Premature loop-unrolling for “performance” usually just makes the code harder to read for no measurable gain.

None of the three methods mutate their input, which pairs well with the broader shift toward closures and pure functions in JavaScript style. If you do need to build data incrementally without creating an array of intermediate objects, a plain loop is still the right tool — these methods are for transformation and aggregation, not every kind of iteration.

Common pitfalls

  • Forgetting the return inside reduce’s callback. Since the callback is often written as an arrow function, forgetting to return the accumulator (or using a block body without return) silently produces undefined on the next iteration.
  • Using forEach when you meant map. forEach returns undefined — it’s for side effects, not for building a new array. If you find yourself pushing into an array from inside a forEach, that’s almost always a map or filter call trying to happen.
  • Calling map for side effects only. If you’re not using the returned array, use forEach or a for...of loop instead — it signals intent more clearly and avoids the wasted allocation.
  • Chaining filter().map() when a single reduce would do, or vice versa. Neither is “correct” — pick whichever chain reads more clearly for the specific transformation.

These methods work on arrays, not on plain objects or Map/Set instances directly — see Map vs Set if you’re working with those collection types and need the equivalent iteration methods.

The takeaway

map transforms, filter selects, reduce collapses — and all three return new values instead of mutating the original array. Reach for map when the output should be the same length as the input, filter when you’re narrowing down to a subset, and reduce only when the result genuinely isn’t an array of the same shape. Chaining them together reads better than a hand-rolled loop in almost every case that matters for everyday application code.

Takina Takina · · 5 min read

Promise.all() vs allSettled() vs race() Compared

Promise.all() fails fast, allSettled() waits for every result, and race() returns whichever promise finishes first — how to choose correctly.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

JavaScript Spread vs Rest Operators, Explained

The spread operator (...) expands an iterable into individual elements; the rest operator collects elements back into an array. Same syntax, opposite jobs.

#JavaScript #Web Development #Frontend
Takina Takina · · 4 min read

ResizeObserver API Explained: Watching Element Size

The ResizeObserver API lets JavaScript watch an element's box size and react without polling or resize-event hacks. How it works and when to use it.

#Web Development #JavaScript #Frontend