Transform lists of data with map, filter, reduce and their friends instead of hand-written loops.
Why this matters
Most of the data a web app handles arrives as a list: products, messages, search results, scores. You constantly need to turn one list into another: show only the items in stock, pull out just the names, add up the total. You can do all of that with for loops, but JavaScript arrays have built-in methods that say what you mean in one line. You will see map, filter and reduce in every codebase and every framework, especially React.
Array basics, quickly
const fruits = ["apple", "banana"];
fruits.push("cherry"); // add to the end -> ["apple", "banana", "cherry"]
fruits.pop(); // remove from the end, returns "cherry"
fruits.includes("apple"); // true
fruits.indexOf("banana"); // 1
fruits.slice(0, 1); // ["apple"] (a copy; the original is unchanged)
Some methods change the array (push, pop, sort, splice); others return a new one and leave the original alone (slice, map, filter). Knowing which is which prevents confusing bugs.
The data for this lesson
const products = [
{ name: "Pen", price: 2, qty: 10, inStock: true },
{ name: "Notebook", price: 5, qty: 0, inStock: false },
{ name: "Backpack", price: 30, qty: 2, inStock: true }
];
Each item is an object with named properties (more on objects in the next lesson). products[0].name is "Pen".
map: transform every item
map calls a function on every item and collects the results into a new array of the same length:
const names = products.map((p) => p.name);
// ["Pen", "Notebook", "Backpack"]
const labels = products.map((p) => `${p.name}: $${p.price}`);
// ["Pen: $2", "Notebook: $5", "Backpack: $30"]
Use map when you want "one output for each input".
filter: keep some items
filter calls a function on every item and keeps the ones where it returns true:
const available = products.filter((p) => p.inStock);
// Pen and Backpack
const cheap = products.filter((p) => p.price < 10);
// Pen and Notebook
The result can be shorter than the original, or empty, but never longer.
reduce: boil a list down to one value
reduce walks the list carrying an accumulator, a running result, and returns its final value:
const totalValue = products.reduce((sum, p) => sum + p.price * p.qty, 0);
// 0 + 2*10 + 5*0 + 30*2 = 80
Read it as: "start sum at 0; for each product, the new sum is the old sum plus price times quantity". Always pass the starting value (the 0 at the end). Without it, reduce uses the first item as the start, which breaks for lists of objects and throws on an empty list.
Chaining
Because map and filter return arrays, you can chain them:
const inStockNames = products
.filter((p) => p.inStock)
.map((p) => p.name);
// ["Pen", "Backpack"]
Each step does one simple thing, and the whole pipeline reads top to bottom.
Other useful methods
| method | returns | example |
|---|---|---|
find | the first matching item, or undefined | products.find((p) => p.name === "Pen") |
some | true if any item matches | products.some((p) => !p.inStock) |
every | true if all items match | products.every((p) => p.price > 0) |
forEach | nothing; just runs code per item | products.forEach((p) => console.log(p.name)) |
sort | the same array, sorted in place | nums.sort((a, b) => a - b) |
A word on sort: without a compare function it sorts as text, so [10, 9, 1].sort() gives [1, 10, 9]. For numbers, always pass (a, b) => a - b. And because sort changes the original array, copy first if you need to keep it: [...nums].sort((a, b) => a - b).
Loop or method?
The two are equivalent. This loop:
const result = [];
for (const p of products) {
if (p.inStock) result.push(p.name);
}
does the same as products.filter((p) => p.inStock).map((p) => p.name). Methods are usually shorter and harder to get wrong (no index, no forgotten push). Loops are still fine when you need break or when the logic is complicated.
Common mistakes
- Forgetting to
returnin a callback with braces.products.map((p) => { p.name })gives[undefined, undefined, undefined]. - Using
mapwhen you meanforEach. If you are not using the new array, you do not needmap. - Leaving out reduce's starting value.
- Sorting numbers without a compare function.
- Expecting
filterto return one item. It always returns an array; usefindfor one item.
Quick recap
maptransforms every item;filterkeeps some;reducecombines into one value.- They return new arrays (or a value) and leave the original alone; chain them for pipelines.
find,some,everyanswer common questions; givesorta compare function for numbers.
Try it
Open the Do tab and write four small functions over a product list using filter, map, reduce and find.