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
- Transform arrays with map
- Keep matching items with filter
- Boil arrays down with reduce
- The accumulator and starting value
- Chaining methods into a pipeline
- Why they never mutate the original
📚 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:
- Download Node.js to run JavaScript on your computer
- Use your browser's Developer Console (Press F12) to test code snippets
- Create a .html file with <script> tags and open it in your browser
🏭 Real-World Analogy: Picture a factory conveyor belt of items:
- • map = a station that reshapes every item as it passes (paint each box red)
- • filter = a quality gate that lets only good items continue
- • reduce = the worker at the end tallying everything into one final number
- • The remembering phrase: map changes, filter chooses, reduce fuses
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: 304️⃣ 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
- 1️⃣ Forgetting to return from a block-body map
- 2️⃣ Using map when you mean forEach
- 3️⃣ Omitting reduce's start value
- 4️⃣ Forgetting to return the accumulator
Quick Reference — map / filter / reduce
| Method | Returns | Use it to… |
|---|---|---|
| map | New array, same length | Transform every item |
| filter | New array, ≤ length | Keep items that pass a test |
| reduce | A single value | Summarise, total, group |
| forEach | undefined | Side effects only (logging) |
Lesson Complete — map, filter & reduce!
- ✅ map transforms — same length, new array
- ✅ filter chooses — keeps items that pass a test
- ✅ reduce fuses — boils the array down to one value
- ✅ Chain them into a clean pipeline; none mutate the original
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
- Previous: Spread & Rest Operators
- Next: Map & Set — The ES6 key-value and unique-value collections — and when to use them
- Quick reference: JavaScript cheat sheet › Arrays
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.