What you'll learn
- Transform data with map, filter and reduce
- Tell array methods that change an array from ones that return a new one
- Chain filter and map into a readable pipeline
- Answer common questions with find, some and every, and sort numbers correctly
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 NotebookThe 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 accumulatorThe running result that reduce passes from one item to the next, starting from the value you supply., 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 = 80Read 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 reduceAn array method that walks the list carrying an accumulator and returns its final value.'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.
Key terms (6)
- map
- An array method that calls a function on every item and returns a new array of the results, the same length as the original.
- filter
- An array method that returns a new array with only the items for which the callback returns true.
- reduce
- An array method that walks the list carrying an accumulator and returns its final value.
- accumulator
- The running result that reduce passes from one item to the next, starting from the value you supply.
- find
- An array method that returns the first item matching a test, or undefined if none does.
- sort
- An array method that sorts in place, as text unless you pass a compare function such as (a, b) => a - b.