JavaScript map, filter and reduce: Transform Arrays Easily
Master the three most useful array methods: map to transform, filter to select, and reduce to combine items, plus how to chain them with real examples.
What you will learn
Loops work, but they are long. To double every number in a list, you must create an empty array, write a loop, calculate, and push the result. JavaScript has three array methods that do these common jobs in one clear line: map (change every item), filter (keep only some items) and reduce (combine everything into one value). In this lesson you will learn how each one works, how to chain them into powerful pipelines, and how to avoid the classic mistakes.
A quick recap
You met these methods briefly in the callbacks lesson. Here is the big picture:
| Method | Question it answers | Returns |
|---|---|---|
map |
“What should each item become?” | A new array, same length |
filter |
“Should this item be kept?” | A new array, same or shorter |
reduce |
“How do I combine all the items?” | One value (a number, text, object, array…) |
All three:
- take a callback function,
- call it once for every item,
- do not change the original array (they return something new).
map: transform every item
map creates a new array by running your function on each item and collecting the results.
const numbers = [1, 2, 3, 4];
const doubled = numbers.map((n) => n * 2);
console.log(doubled); // [2, 4, 6, 8]
console.log(numbers); // [1, 2, 3, 4] (unchanged)
The loop version, for comparison:
const numbers = [1, 2, 3, 4];
const doubledLoop = [];
for (const n of numbers) {
doubledLoop.push(n * 2);
}
console.log(doubledLoop); // [2, 4, 6, 8]
map says what you want, and the loop says how to do it.
The callback arguments
Your function receives three things (you usually need only the first one or two):
array.map((item, index, array) => { ... });
const names = ["Riya", "Karan", "Neha"];
const numbered = names.map((name, index) => `${index + 1}. ${name}`);
console.log(numbered); // ["1. Riya", "2. Karan", "3. Neha"]
Pulling out one property
A very common use: take a list of objects and get one field from each.
const users = [
{ name: "Riya", age: 20 },
{ name: "Karan", age: 25 },
{ name: "Neha", age: 22 }
];
const names = users.map((user) => user.name);
console.log(names); // ["Riya", "Karan", "Neha"]
Changing the shape of objects
const products = [
{ name: "Notebook", price: 100 },
{ name: "Pen", price: 20 }
];
const discounted = products.map((p) => ({
name: p.name,
price: p.price * 0.9
}));
console.log(discounted);
// [{ name: "Notebook", price: 90 }, { name: "Pen", price: 18 }]
console.log(products[0].price); // 100 (original untouched)
Notice the round brackets around the object ({ ... }). Without them, JavaScript reads { } as a function body (as you saw in the arrow functions lesson).
map vs forEach
- Use
mapwhen you want a new array of results. - Use
forEachwhen you only want to do something (print, save) and need nothing back.
[1, 2, 3].forEach((n) => console.log(n)); // does something, returns undefined
const result = [1, 2, 3].map((n) => n * 2); // gives back [2, 4, 6]
filter: keep only what you want
filter creates a new array with only the items for which your function returns a truthy value. A function that answers yes or no like this is called a predicate.
const numbers = [1, 2, 3, 4, 5, 6];
const evens = numbers.filter((n) => n % 2 === 0);
console.log(evens); // [2, 4, 6]
console.log(numbers); // [1, 2, 3, 4, 5, 6] (unchanged)
The items themselves are not changed, only chosen. If nothing matches, you get an empty array [] (never undefined).
const marks = [72, 38, 90, 45, 28];
console.log(marks.filter((m) => m >= 40)); // [72, 90, 45]
console.log(marks.filter((m) => m > 100)); // []
Filtering objects
const products = [
{ name: "Notebook", price: 80, inStock: true },
{ name: "Headphones", price: 1500, inStock: true },
{ name: "Backpack", price: 900, inStock: false }
];
const available = products.filter((p) => p.inStock);
const cheap = products.filter((p) => p.price < 1000);
const cheapAndAvailable = products.filter((p) => p.inStock && p.price < 1000);
console.log(available.length); // 2
console.log(cheap.length); // 2
console.log(cheapAndAvailable.length); // 1
Removing falsy values with filter(Boolean)
const data = ["Riya", "", null, "Karan", undefined, "Neha", 0];
console.log(data.filter(Boolean)); // ["Riya", "Karan", "Neha"]
Boolean is a function that turns any value into true or false, so it works as a ready-made predicate.
Removing one item (without changing the original)
const todos = [
{ id: 1, task: "Buy milk" },
{ id: 2, task: "Study JS" },
{ id: 3, task: "Walk" }
];
const idToRemove = 2;
const remaining = todos.filter((todo) => todo.id !== idToRemove);
console.log(remaining.map((t) => t.task)); // ["Buy milk", "Walk"]
console.log(todos.length); // 3 (original untouched)
This is the standard way to “delete” an item in modern apps.
Search as you type
const cities = ["Delhi", "Mumbai", "Dehradun", "Chennai", "Dubai"];
const search = "de";
const matches = cities.filter((city) =>
city.toLowerCase().includes(search.toLowerCase())
);
console.log(matches); // ["Delhi", "Dehradun"]
Unique values
const tags = ["js", "html", "js", "css", "html"];
const unique = tags.filter((tag, index) => tags.indexOf(tag) === index);
console.log(unique); // ["js", "html", "css"]
An item is kept only if its first position equals the current position. (The shorter way is [...new Set(tags)].)
reduce: combine into one value
reduce is the most powerful (and the most confusing) of the three. It walks through the array, keeping a running result called the accumulator, and gives you back one final value.
const result = array.reduce((accumulator, item) => newAccumulator, startValue);
Step by step: adding numbers
const numbers = [10, 20, 30];
const sum = numbers.reduce((total, n) => total + n, 0);
console.log(sum); // 60
Read it as: “start with 0. For each number, add it to the total.” Here is what happens inside:
| Round | total (before) |
n |
Returned (becomes next total) |
|---|---|---|---|
| 1 | 0 (the start value) |
10 | 10 |
| 2 | 10 | 20 | 30 |
| 3 | 30 | 30 | 60 |
After the last round, reduce returns 60. Whatever your function returns becomes the accumulator for the next round.
The same job with a loop
const numbers = [10, 20, 30];
let total = 0;
for (const n of numbers) {
total = total + n;
}
console.log(total); // 60
The accumulator is total, and the start value is 0. Once you see that, reduce is just a loop with the variable tucked inside.
More examples with numbers
const prices = [250, 99, 1499, 75];
// Product of all numbers
console.log([2, 3, 4].reduce((acc, n) => acc * n, 1)); // 24
// Largest number
const highest = prices.reduce((max, p) => (p > max ? p : max), prices[0]);
console.log(highest); // 1499
// Average
const average = prices.reduce((sum, p) => sum + p, 0) / prices.length;
console.log(average); // 480.75
The start value can be anything
The accumulator does not have to be a number. It can be a string, an array or an object. That is where reduce really shines.
Count how often each item appears (object):
const fruits = ["apple", "banana", "apple", "mango", "apple", "banana"];
const counts = fruits.reduce((result, fruit) => {
result[fruit] = (result[fruit] || 0) + 1;
return result;
}, {});
console.log(counts); // { apple: 3, banana: 2, mango: 1 }
Group items by a property (object of arrays):
const products = [
{ name: "Notebook", category: "stationery" },
{ name: "Headphones", category: "electronics" },
{ name: "Pen", category: "stationery" },
{ name: "Mouse", category: "electronics" }
];
const grouped = products.reduce((groups, product) => {
const key = product.category;
if (!groups[key]) {
groups[key] = [];
}
groups[key].push(product.name);
return groups;
}, {});
console.log(grouped);
// { stationery: ["Notebook", "Pen"], electronics: ["Headphones", "Mouse"] }
Build a lookup by id:
const users = [
{ id: 101, name: "Riya" },
{ id: 102, name: "Karan" }
];
const usersById = users.reduce((lookup, user) => {
lookup[user.id] = user;
return lookup;
}, {});
console.log(usersById[102].name); // "Karan"
Flatten an array of arrays (array accumulator):
const nested = [[1, 2], [3], [4, 5]];
const flat = nested.reduce((all, group) => all.concat(group), []);
console.log(flat); // [1, 2, 3, 4, 5]
Always give reduce a start value
Without a start value, reduce uses the first item as the start and begins from the second. That works for sums, but it breaks easily:
console.log([].reduce((a, b) => a + b)); // TypeError: Reduce of empty array with no initial value
console.log([].reduce((a, b) => a + b, 0)); // 0 (safe)
Also, if your accumulator is an object or array, you must provide the start value ({} or []), or the first item would be used by mistake.
Do not forget to return
The most common reduce bug is forgetting to return the accumulator:
const wrong = [1, 2, 3].reduce((total, n) => {
total + n; // no return! total becomes undefined in the next round
}, 0);
console.log(wrong); // undefined
reduceRight
reduceRight works the same, but starts from the last item and goes backwards. You will rarely need it.
Chaining: combining the three
Because map and filter return arrays, you can call another method immediately on the result. This is called method chaining.
const products = [
{ name: "Notebook", price: 80, inStock: true },
{ name: "Headphones", price: 1500, inStock: true },
{ name: "Backpack", price: 900, inStock: false },
{ name: "Pen", price: 20, inStock: true }
];
const total = products
.filter((p) => p.inStock) // keep only items in stock
.map((p) => p.price) // take just the prices
.reduce((sum, price) => sum + price, 0); // add them up
console.log(total); // 1600
Read it like a recipe, from top to bottom: “Take the products, keep those in stock, get their prices, add them up.”
How the data flows
products → filter → → map → → reduce →
[4 objects] [3 objects] [80, 1500, 20] 1600
Each step receives the output of the step before.
Tips for good chains
- Put
filterfirst, so the later steps have fewer items to work on. - One step per line, with the dot at the start of each line, so it reads like a list of instructions.
- Debug by splitting the chain into variables or by adding a
console.login the middle:
const products = [
{ name: "Notebook", price: 80, inStock: true },
{ name: "Backpack", price: 900, inStock: false }
];
const result = products
.filter((p) => p.inStock)
.map((p) => {
console.log("Mapping", p.name); // peek inside the chain
return p.price;
});
console.log(result); // [80]
- You can add
sortorslicein a chain too. Copy the array first if you callsort, because it changes the array:[...products].sort(...).
When to use a loop instead
map, filter and reduce always walk through every item. Prefer a normal for...of loop when you need to:
- stop early with
break(for example, find the first match), - skip items with
continue, - or run asynchronous code step by step with
await(a later lesson).
For stopping at the first match, the next lesson introduces find and some.
Real-life use cases
1. Show a list on the page
const items = ["HTML", "CSS", "JavaScript"];
const html = items.map((item) => `<li>${item}</li>`).join("");
console.log(html); // "<li>HTML</li><li>CSS</li><li>JavaScript</li>"
map builds a piece of HTML for each item, and join("") glues them into one string. You can then put that string into the page using innerHTML (the DOM lessons explain it). This is how many small apps draw lists.
2. Shopping cart total with quantities
const cart = [
{ name: "Notebook", price: 80, qty: 3 },
{ name: "Pen", price: 20, qty: 5 },
{ name: "Bag", price: 900, qty: 1 }
];
const total = cart.reduce((sum, item) => sum + item.price * item.qty, 0);
console.log(`Total: ₹${total}`); // "Total: ₹1240"
3. Total after discount and tax (a full chain)
const prices = [100, 250, 400, 50];
const finalTotal = prices
.filter((price) => price >= 100) // only items of ₹100 or more
.map((price) => price * 0.9) // 10% discount
.map((price) => price * 1.18) // 18% GST
.reduce((sum, price) => sum + price, 0);
console.log(finalTotal.toFixed(2)); // "796.50"
4. Average rating
const ratings = [5, 4, 3, 5, 4, 4];
const average = ratings.reduce((sum, r) => sum + r, 0) / ratings.length;
console.log(average.toFixed(1)); // "4.2"
5. Count orders by status
const orders = [
{ id: 1, status: "delivered" },
{ id: 2, status: "pending" },
{ id: 3, status: "delivered" },
{ id: 4, status: "cancelled" },
{ id: 5, status: "delivered" }
];
const byStatus = orders.reduce((counts, order) => {
counts[order.status] = (counts[order.status] || 0) + 1;
return counts;
}, {});
console.log(byStatus); // { delivered: 3, pending: 1, cancelled: 1 }
6. Names of the most expensive products
const products = [
{ name: "Notebook", price: 80 },
{ name: "Headphones", price: 1500 },
{ name: "Backpack", price: 900 },
{ name: "Pen", price: 20 }
];
const premium = products
.filter((p) => p.price >= 500)
.map((p) => p.name.toUpperCase());
console.log(premium); // ["HEADPHONES", "BACKPACK"]
7. Format prices for display
const prices = [1999, 49.5, 120000];
const formatted = prices.map((price) =>
price.toLocaleString("en-IN", { style: "currency", currency: "INR" })
);
console.log(formatted); // ["₹1,999.00", "₹49.50", "₹1,20,000.00"]
8. Update one item in a list without changing the original
const todos = [
{ id: 1, task: "Buy milk", done: false },
{ id: 2, task: "Study JS", done: false }
];
const updated = todos.map((todo) =>
todo.id === 2 ? { id: todo.id, task: todo.task, done: true } : todo
);
console.log(updated[1].done); // true
console.log(todos[1].done); // false (original untouched)
map returns each item as it is, except the one we want to change. Apps built with React use this exact pattern to update state.
9. Convert text to numbers safely
const input = ["10", "20", "abc", "30"];
const numbers = input.map(Number).filter((n) => !Number.isNaN(n));
console.log(numbers); // [10, 20, 30]
Common mistakes
- Forgetting
returnin the callback. With braces{ }, you must writereturn. Otherwisemapgives[undefined, ...], andreducebreaks. - Using
mapwhen you do not need a new array. UseforEachfor simple actions. - Using
filterand expecting the items to change. It only selects items. - Forgetting the start value in
reduce. An empty array throws aTypeError, and objects as accumulators break. - Forgetting to
returnthe accumulator from thereducecallback. - Passing a function with the wrong parameters.
["1","2","3"].map(parseInt)gives[1, NaN, NaN]. Use.map(Number). - Modifying the original objects inside
map.map((p) => { p.price = 0; return p; })changes the original objects. Return a new object instead. - Putting
filterlast in a long chain. Filter first to avoid unnecessary work. - Writing a
reducethat is hard to read. If a chain ofmapandfilteris clearer, use that. Usereducefor sums, counting, grouping and building objects. - Forgetting round brackets when returning an object from an arrow function:
(p) => ({ ... }). - Calling these methods on something that is not an array.
undefined.map(...)crashes. Make sure the value is an array.
Practice
- Use
mapto turn[1, 2, 3, 4, 5]into their squares. - Use
mapto turn["riya", "karan", "neha"]into capitalised names ("Riya", …). - Use
filterto keep the numbers greater than 10 from[5, 12, 8, 130, 44]. - Use
filterto keep only words with 5 or more letters from["sun", "planet", "moon", "galaxy", "star"]. - Use
reduceto calculate the sum of[12, 45, 7, 89, 23], and then the average. - Given a list of products with
name,priceandinStock, print the names of the in-stock products that cost less than ₹500, using a chain. - Use
reduceto count the vowels in"javascript"(hint:[..."javascript"]makes an array of letters). - Use
reduceto turn[["a", 1], ["b", 2]]into the object{ a: 1, b: 2 }. - Write a chain that takes
["10", "x", "25", "", "40"], converts to numbers, removes the invalid ones, and returns their total. - Challenge: given an array of orders (
{ customer, amount }), usereduceto produce an object with each customer’s total amount.
Recap
maptransforms every item and returns a new array of the same length.filterkeeps the items for which the callback returns a truthy value, and returns a new array (possibly empty).reducecombines all items into one value. Give it a start value, and always return the accumulator.- None of the three changes the original array. They are the building blocks of clean, modern JavaScript.
- Use
mapto change,filterto choose,reduceto combine, andforEachjust to do something. - Chain them for pipelines. Put
filterfirst, one step per line. - Use a normal loop when you need
break,continueorawait. - Next you will learn how to search and test arrays with
find,findIndex,some,everyandincludes.