Topic 5.4
map and filter
In one line
map makes a new array by changing every item with a function you give it, and filter makes a new array keeping only the items your function says yes to; neither changes the original, and they chain together into clear data pipelines.
Think of it like this
A juice stall. map is the juicer: put in 5 oranges, get out 5 glasses of juice, one for each orange, every one changed the same way. filter is the person sorting fruit: put in 5 oranges, get out only the good ones (maybe 3), each one untouched. You can line them up: sort first, then juice what's left.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- map
- Makes a new array of the same length by transforming every item.
- filter
- Makes a new array containing only the items that pass a test.
- Transform
- Turn one value into another, for example a price into a price with tax.
- Truthy
- A value that counts as true in a condition: everything except
false,0,"",null,undefinedandNaN. - Chaining
- Calling one method on the result of another:
arr.filter(...).map(...). - Implicit return
- An arrow function without curly braces returns its expression automatically:
(n) => n * 2. - Pure callback
- A callback that only uses its inputs and returns a value, without changing anything outside it.
Step by step
01map: one in, one out
map hands each element to your function and collects what comes back.
const prices = [100, 250, 40];
const half = prices.map((p) => p / 2);
// half is [ 50, 125, 20 ]
// prices is still [ 100, 250, 40 ]02filter: keep or drop
Return true to keep an item, false to drop it.
const cheap = prices.filter((p) => p < 200);
// cheap is [ 100, 40 ]03See the shapes
map keeps the length and changes the values. filter keeps the values and changes the length.
04Chain them
Each step gets the array the previous step returned. Put each call on its own line so it reads like a list of steps.
const names = products
.filter((p) => p.inStock)
.map((p) => p.name);05Remember to return
Short arrow: the value is returned for you. Arrow with { }: write return. Returning an object from a short arrow: wrap it in ( ).
nums.map((n) => n * 2); // ok
nums.map((n) => { return n * 2; }); // ok
nums.map((n) => { n * 2; }); // [ undefined, ... ]
nums.map((n) => ({ value: n })); // objectsTry it yourself
- 1
Add tax
In the first example, make
withTaxby adding ₹10 to every price. Predict the array, then log it next topricesto provepricesdidn't change. - 2
Filter by price
In the second example, change the filter so the checkbox shows only products under ₹100. Predict which names the page will list.
- 3
Fix parseInt
In the third example, change
.map(parseInt)to.map((s) => parseInt(s, 10)). Predict the output first.
Code & diagrams
`map` always gives 3 items back for 3 items in. `filter` gives back anywhere from 0 to 3.
Console
(nothing was printed)Expected console output
[ 100, 250, 40 ] [ 50, 125, 20 ]
[ 100, 40 ]
[ '0: ₹100', '1: ₹250', '2: ₹40' ]
[ true, true, false ]
[]Tick the box: `filter` decides what's shown, `map` turns each product into an `<li>`. The `products` array itself never changes.
Preview
Console
Nothing logged yet.Expected console output
[ 'PEN', 'BAG' ]
3`map(parseInt)` passes the index as `parseInt`'s second argument (the radix). Braces without `return` give `undefined`. `filter(Boolean)` drops every falsy value. `map` skips holes and keeps them.
Console
(nothing was printed)Expected console output
[ 1, NaN, NaN ]
[ 1, 2, 3 ]
[ undefined, undefined, undefined ]
[ { value: 1 }, { value: 2 }, { value: 3 } ]
[ 'hi', 5 ]
[ 10, <empty>, 30 ]Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Curly braces without return
In the first example, write prices.map((p) => { p / 2; }).
Break #2
Calling map on a NodeList
Write document.querySelectorAll("li").map((li) => li.textContent).
Myth vs fact
Myth
map changes the original array.
Fact
map always returns a new array and leaves the original alone. (Your callback could still change the objects inside it, which is why callbacks should be pure.)
Myth
filter returns undefined or null when nothing matches.
Fact
It returns an empty array []. You can safely call .length or .map on the result.
Myth
map and forEach are interchangeable.
Fact
map builds and returns a new array; forEach returns undefined. Using map only for side effects wastes an array and confuses readers.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
Every
map/filterin a chain creates a new intermediate array. For a few thousand items that's nothing. For millions of items in a hot loop, a singleforloop orreduce, or iterator helpers (arr.values().filter(...).map(...), standardised in ES2025 and available in current browsers), avoids the intermediate arrays. - ▸
mapreadslengthonce at the start; items you push to the array during the call aren't visited. Holes are skipped bymap,filterandforEach, butmapkeeps the hole in its result, whilefilterdrops it. - ▸
mapbuilds its result with the array's species constructor, so subclasses ofArrayget subclass instances back. Typed arrays'mapreturns the same typed array kind, sonew Uint8Array([200]).map((n) => n * 2)gives144(wrapped around at 256). - ▸
In React and similar libraries,
items.map((item) => <li key={item.id}>…</li>)is how lists are drawn. Thekeyshould be a stable id from the data, not themapindex, or items get mixed up when the list is reordered. The React course (/react, Topic 3.3) shows that bug live.
Remember this
- 1
arr.map(fn)callsfnonce for every element, in order, and builds a new array from whatfnreturns. The new array always has the same length as the original.[1, 2, 3].map((n) => n * 10)gives[10, 20, 30]. The original array is not changed. Usemapwhenever you think "turn each X into a Y": numbers into prices, objects into names, data into list items. - 2
arr.filter(fn)callsfnfor every element and builds a new array of the elements for whichfnreturned a truthy value (Phase 2: anything that isn'tfalse,0,"",null,undefinedorNaN). The kept elements are the same values, not copies, and keep their order. The result can be shorter, and is[]if nothing passes; it's neverundefined. - 3
Both pass three arguments to your callback:
(element, index, array). Most of the time you only use the first. The index is useful for numbering:names.map((name, i) => (i + 1) + ". " + name). - 4
Because both return arrays, you can chain them:
products.filter((p) => p.inStock).map((p) => p.name). Read it top to bottom like a recipe: "take the products, keep the ones in stock, turn each into its name". Filter first when you can, somapdoes less work. - 5
The most common bug with
mapis forgetting to return. An arrow function with curly braces{ }needs an explicitreturn; without it, it returnsundefined, and you get an array full ofundefined. To return an object literal from a short arrow, wrap it in parentheses:(n) => ({ value: n }), because bare{starts a function body. - 6
Use
maponly when you want the new array. If you only want to do something for each item (log it, change the page) and ignore the result, useforEachorfor...of(Topic 5.7). And keep the callbacks pure (Phase 3): don't push to outside arrays or change the items inside amap; return new values instead.
Explain it without notes
What is the difference between map and filter?
Why does ["1", "2", "3"].map(parseInt) return [1, NaN, NaN]?
Why should you filter before mapping when you can?
When should you not use map?
Practice
Given const names = ["asha", "ravi"];, make ["Asha", "Ravi"] with map.
Given an array of { title, done } tasks, print the titles of the tasks that are not done.
Make an array of the even numbers from [1, 2, 3, 4, 5, 6], each squared.
Trade-offs
- ↔
Chains of
filterandmapare clear and easy to change; a single hand-written loop is a little faster and uses less memory for huge arrays, but is harder to read. - ↔
filter(Boolean)is a neat way to drop empty values, but it also drops0and"", which may be real data. Write the test explicitly ((x) => x != null) when those values matter.
Done when you can
I can transform every item of an array with
map.I can keep only matching items with
filter.I can chain
filterandmapand read the chain step by step.I remember to
returnin arrow functions with braces, and wrap object literals in( ).I can explain the
map(parseInt)trap.