Array Methods: map, filter, reduce

The transformations you will reach for in every list you render.

What you'll learn

  • map, filter, and reduce, and how to tell which one you need
  • Why these return new arrays instead of changing the original
  • Chaining transformations into a readable pipeline
  • The mutating methods to be careful with

Why these three

Rendering a list in React means turning an array of data into an array of elements. You do not write a loop and push into an array — you describe the transformation. These three cover nearly all of it:

  • map — same number of items, each one transformed
  • filter — fewer items, each one unchanged
  • reduce — many items in, a single value out

Each returns a new array (or value) and leaves the original alone.

map

map calls your function once per item and collects the results.

const numbers = [1, 2, 3, 4];

const doubled = numbers.map((n) => n * 2);

console.log("original:", numbers);
console.log("doubled: ", doubled);

const users = [{ name: "Ada" }, { name: "Grace" }];
console.log(users.map((user) => user.name));

The callback also receives the index, which is how list rendering in React gets its position when there is no better identifier:

const items = ["first", "second", "third"];

const numbered = items.map((item, index) => index + 1 + ". " + item);

numbered.forEach((line) => console.log(line));

map vs forEach

forEach runs your function for its side effects and returns undefined. map returns the transformed array. If you want a result, use map:

const numbers = [1, 2, 3];

const fromForEach = numbers.forEach((n) => n * 2);
const fromMap = numbers.map((n) => n * 2);

console.log("forEach returns:", fromForEach);
console.log("map returns:    ", fromMap);

filter

filter keeps the items for which your function returns a truthy value.

const numbers = [1, 2, 3, 4, 5, 6];

console.log(numbers.filter((n) => n % 2 === 0));

const users = [
  { name: "Ada", active: true },
  { name: "Grace", active: false },
  { name: "Alan", active: true },
];

const active = users.filter((user) => user.active);
console.log(active.map((user) => user.name));

A common use is removing an item without mutating the array — the pattern behind almost every "delete" button in a React app:

const todos = [
  { id: 1, text: "Learn map" },
  { id: 2, text: "Learn filter" },
  { id: 3, text: "Learn reduce" },
];

const remaining = todos.filter((todo) => todo.id !== 2);

console.log("kept:", remaining.map((todo) => todo.text));
console.log("original length:", todos.length);

reduce

reduce folds an array down to a single value. It takes an accumulator and the current item, and returns the next accumulator. The second argument is the starting value.

const numbers = [1, 2, 3, 4];

const total = numbers.reduce((sum, n) => sum + n, 0);
console.log("total:", total);

const max = numbers.reduce((best, n) => (n > best ? n : best), numbers[0]);
console.log("max:  ", max);

The result does not have to be a number. Grouping items into an object is one of the most useful things reduce does:

const people = [
  { name: "Ada", team: "core" },
  { name: "Grace", team: "tools" },
  { name: "Alan", team: "core" },
];

const byTeam = people.reduce((groups, person) => {
  const existing = groups[person.team] || [];
  return { ...groups, [person.team]: [...existing, person.name] };
}, {});

console.log(byTeam);

Always pass the initial value. Without it, reduce uses the first item as the starting accumulator — and throws on an empty array.

Chaining

Because each method returns a new array, they compose into a pipeline that reads top to bottom:

const orders = [
  { item: "Book", price: 12, shipped: true },
  { item: "Pen", price: 3, shipped: false },
  { item: "Desk", price: 250, shipped: true },
];

const shippedTotal = orders
  .filter((order) => order.shipped)
  .map((order) => order.price)
  .reduce((sum, price) => sum + price, 0);

console.log("shipped total:", shippedTotal);

Filter before you map when you can — there is no point transforming items you are about to discard.

Methods that mutate

Some array methods change the array in place. They are worth knowing precisely so you can avoid them on data you do not own:

const original = [3, 1, 2];

const sorted = original.sort();
console.log("sort returns the same array:", sorted === original);
console.log("original is now:", original);

// Copy first to leave the input alone.
const safe = [...original].reverse();
console.log("copy:    ", safe);
console.log("original:", original);

push, pop, splice, sort, and reverse all mutate. map, filter, slice, concat, and spread all copy.

Common mistakes

  • Using forEach when you want a result. It always returns undefined.
  • Forgetting reduce's initial value. It changes the behaviour and throws on empty arrays.
  • Sorting state directly. sort mutates; copy first.
  • Forgetting the callback's return. (n) => { n * 2 } returns nothing — drop the braces or add return.

Exercise

Produce the names of active users with more than 10 points, in one chain. Expected output: ["Ada", "Alan"].

const users = [
  { name: "Ada", active: true, points: 42 },
  { name: "Grace", active: false, points: 90 },
  { name: "Alan", active: true, points: 15 },
  { name: "Edsger", active: true, points: 4 },
];

// Your turn: filter, then map.
const result = users;

console.log(result);