map, filter & reduce

Reviewed & published by Brayan K

These three array methods are the heart of modern, professional JavaScript. They let you express "transform this", "keep these", and "summarise that" declaratively — you describe the result instead of writing fiddly for loops with index bookkeeping.

Part of the free JavaScript course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

Master this trio and you'll read and write real-world data code — turning API responses into UI, totals, and reports — with confidence.

What You'll Learn in This Lesson

📚 Prerequisites: You should be comfortable with arrays and functions — especially arrow functions, since the callbacks here are almost always arrows.

💡 Running Code Locally: While this online editor runs real JavaScript, some advanced examples may have limitations. For the best experience:

🏭 Real-World Analogy: Picture a factory conveyor belt of items:

1️⃣ map — Transform Every Item

map runs your callback on every element and collects the returned values into a new array of the same length. The original is untouched. Use it whenever you need "the same list, but reshaped" — prices with tax, names from user objects, labels for a dropdown.

const prices = [10, 20, 30];

// Add 20% tax to each price (returns a NEW array)
const withTax = prices.map(p => p * 1.2);
console.log(withTax); // ✅ Expected output: [ 12, 24, 36 ]
console.log(prices);  // ✅ Expected output: [ 10, 20, 30 ]  (original unchanged)

// Pull one field out of objects
const users = [{ name: "Ada" }, { name: "Bo" }, { name: "Cy" }];
const names = users.map(u => u.name);
console.log(names); // ✅ Expected output: [ 'Ada', 'Bo', 'Cy' ]

2️⃣ filter — Keep What Passes the Test

filter keeps every item for which your callback returns a truthy value and drops the rest. The result is a new array that's the same length or shorter — never longer, and never reshaped. Think "show only the items matching this condition".

const numbers = [4, 9, 12, 7, 20, 3];

// Keep only the even numbers
const evens = numbers.filter(n => n % 2 === 0);
console.log(evens); // ✅ Expected output: [ 4, 12, 20 ]

// Filter objects by a condition
const products = [
  { name: "Pen", stock: 0 },
  { name: "Pad", stock: 5 },
  { name: "Ink", stock: 0 },
];
const inStock = products.filter(p => p.stock > 0);
console.log(inStock); // ✅ Expected output: [ { name: 'Pad', stock: 5 } ]

🎯 Your Turn #1 — Double the Evens

Filter to the even numbers, then map them to double their value.

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

// 👉 keep the evens, then double each one
const result = nums
  .filter(n => n % 2 === ___)  // even test
  .map(n => n * ___);          // double

console.log(result); // ✅ Expected output: [ 4, 8, 12 ]

3️⃣ reduce — Boil It Down to One Value

reduce is the most powerful of the three. It carries an accumulator from item to item, and your callback returns the updated accumulator each time. The signature is array.reduce((acc, item) => newAcc, startValue). Always pass a start value — it sets the type and protects against empty arrays.

const cart = [3.5, 2.0, 4.25];

// Sum: start at 0, add each price to the running total
const total = cart.reduce((acc, price) => acc + price, 0);
console.log(total); // ✅ Expected output: 9.75

// Find the max with reduce
const temps = [18, 25, 21, 30, 17];
const hottest = temps.reduce((acc, t) => (t > acc ? t : acc), -Infinity);
console.log(hottest); // ✅ Expected output: 30

4️⃣ reduce Into an Object — Grouping & Tallying

The accumulator doesn't have to be a number. Start with an empty object {} and you can group, count, or index data — patterns you'll reach for constantly when shaping API responses for the screen.

const votes = ["yes", "no", "yes", "yes", "no"];

// Tally each answer into an object
const tally = votes.reduce((acc, v) => {
  acc[v] = (acc[v] || 0) + 1;
  return acc;
}, {});

console.log(tally); // ✅ Expected output: { yes: 3, no: 2 }

5️⃣ Chaining Into a Pipeline

Because filter and map return arrays, you can chain them and finish with reduce. Reading top to bottom, the data flows through each stage like a pipeline — this is the everyday shape of real data code.

const orders = [
  { item: "Book", price: 12, paid: true },
  { item: "Pen", price: 3, paid: false },
  { item: "Lamp", price: 40, paid: true },
];

// Total revenue from PAID orders only
const revenue = orders
  .filter(o => o.paid)          // keep paid orders
  .map(o => o.price)            // pull out the prices
  .reduce((sum, p) => sum + p, 0); // add them up

console.log(revenue); // ✅ Expected output: 52

🎯 Your Turn #2 — Sum a List

Complete the reduce so it adds every number, starting from 0.

const amounts = [100, 250, 75, 25];

// 👉 add each amount to the accumulator, starting from 0
const total = amounts.reduce((acc, n) => acc ___ n, ___);

console.log(total); // ✅ Expected output: 450

🧩 Reorder Challenge

These lines are scrambled. Reorder them so the pipeline logs 30.

Why: each stage feeds the next — filter (→ [2,4]) before map (→ [4,8]) before reduce (→ 30) before logging.

🧠 Quick Recall

Predict the output before revealing the answer.

[ 2, 3, 4 ] — map returns a new array of the same length, each item transformed.

2 — only 8 and 11 pass the test, so the new array has length 2.

10 — reduce sums the array starting from 0: 0+1+2+3+4.

Common Errors & Fixes

Quick Reference — map / filter / reduce

MethodReturnsUse it to…
mapNew array, same lengthTransform every item
filterNew array, ≤ lengthKeep items that pass a test
reduceA single valueSummarise, total, group
forEachundefinedSide effects only (logging)

Lesson Complete — map, filter & reduce!

Practice quiz

What does map return?

  • A single value
  • The original array, mutated
  • A new array of the same length, each item transformed
  • Only the items that pass a test

Answer: A new array of the same length, each item transformed. map transforms every element and returns a new array of the same length.

What does [1, 2, 3].map(n => n + 1) log?

  • [2, 3, 4]
  • [1, 2, 3]
  • 6
  • [1, 2, 3, 4]

Answer: [2, 3, 4]. map adds 1 to each element, producing [2, 3, 4].

What does filter keep?

  • The first matching item only
  • Items reshaped by the callback
  • A single accumulated value
  • Items for which the callback returns a truthy value

Answer: Items for which the callback returns a truthy value. filter keeps every item whose callback returns truthy and drops the rest.

What is [5, 8, 11].filter(n => n > 7).length?

  • 1
  • 2
  • 3
  • 0

Answer: 2. Only 8 and 11 pass the test, so the new array has length 2.

What does [1, 2, 3, 4].reduce((a, b) => a + b, 0) return?

  • 10
  • 0
  • 4
  • [1, 2, 3, 4]

Answer: 10. reduce sums the array starting from 0: 0+1+2+3+4 = 10.

What is the second argument to reduce?

  • The callback
  • The array length
  • The starting value of the accumulator
  • The current index

Answer: The starting value of the accumulator. The second argument is the start value; always pass it so empty arrays do not throw and the type is clear.

Do map and filter mutate the original array?

  • Yes, both mutate
  • No, both return a new array
  • Only filter mutates
  • Only map mutates

Answer: No, both return a new array. Both are non-mutating: they return a brand-new array and leave the original unchanged.

Why might a map produce all undefined values?

  • The array was empty
  • filter was used instead
  • reduce had no start value
  • The callback uses a block body { } without a return

Answer: The callback uses a block body { } without a return. A block-body arrow like n => { n * 2; } returns nothing; use n => n * 2 or add an explicit return.

What does orders.filter(o => o.paid).map(o => o.price).reduce((s, p) => s + p, 0) compute?

  • The count of paid orders
  • The total price of paid orders only
  • All prices summed
  • The most expensive order

Answer: The total price of paid orders only. The pipeline keeps paid orders, pulls their prices, then sums them.

Which method should you use only for side effects like logging?

  • map
  • filter
  • forEach
  • reduce

Answer: forEach. forEach returns undefined and is meant for side effects; using map for logging builds a junk array.

Continue this course

Frequently asked questions

When should I use map vs filter vs reduce?

Use map to TRANSFORM each item into a new item (same length out). Use filter to KEEP only the items that pass a test (same length or shorter). Use reduce to BOIL the whole array down to a single value — a total, an object, a grouped structure, anything. A handy rhyme: map changes, filter chooses, reduce fuses.

Do map and filter change the original array?

No. Both are non-mutating: they always return a brand-new array and leave the original exactly as it was. This is why they pair so well with immutable patterns in React and Redux. forEach, by contrast, returns nothing and is only for side effects.

What is the accumulator in reduce?

The accumulator is the running result that reduce carries from one item to the next. On each step your callback receives (accumulator, currentItem) and returns the new accumulator. The second argument to reduce is the starting value — always provide it so empty arrays don't throw and the starting type is clear.

Why is my map full of undefined?

You probably forgot to return from the callback. With a block body {} you must write 'return value' explicitly; with a concise arrow body the value is returned automatically. Either x => x * 2 or x => { return x * 2; } — but not x => { x * 2; }, which returns undefined.

Can I chain these methods together?

Yes, and it's idiomatic. Because filter and map return arrays, you can write data.filter(...).map(...).reduce(...) to read top to bottom like a pipeline. Just be aware each step walks the array again; for very large data that occasionally matters, but for everyday sizes clarity wins.

Related lessons