map and filter

map turns every item of an array into something new, filter keeps only the items that pass a test, and chained together they turn raw data into exactly the list a screen needs.

Apprentice · JavaScript · Updated

After this lesson you can

  • Turn every item of an array into something new with map
  • Keep only the items that pass a test with filter
  • Chain filter and map to answer a question about a list in one expression

Key takeaways

  • map calls your callback on every item and gives back a new array of whatever the callback returned, always the same length as the original.
  • filter calls your callback on every item and keeps the ones it returns true for. When none pass, you get an empty array.
  • Neither map nor filter changes the original array. Each gives back a new one.
  • Because filter and map both give back an array, you can chain them, one after the other, and the order matters.
  • An arrow function with curly braces needs its own return. Without it, map fills the new array with undefined.

Open a music app and tap Liked songs: you see only the songs you liked. Open a shop and every price shows up as $12.99, even though the data holds plain numbers. Search for "pizza" and you see only the places that match. Almost every list you see on a screen started as raw data that was filtered (keep some items) and mapped (turn each item into what the screen shows). In JavaScript those two jobs are two array methods, map and filter, and both take a callback, the function you hand over from Functions as Values: Callbacks.

Every example here uses the Comet Dash high-score list: gamer tags, scores and the level each player reached.

map: a stamp on every envelope

Picture a stamping machine at the post office. Every envelope goes in, every envelope comes out stamped, and the same number comes out as went in.

map is that machine for arrays. It calls your callback once for each item, and builds a new array out of whatever the callback returns:

const scores = [8200, 12450, 3100];

const doubled = scores.map((score) => score * 2);

console.log(doubled);
console.log(scores);
[ 16400, 24900, 6200 ]
[ 8200, 12450, 3100 ]

Read scores.map(...) as "scores, mapped through this function". The callback (score) => score * 2 gets one score at a time and returns it doubled. The new array goes in doubled, and scores is left exactly as it was.

map with real records: from data to display lines

Most of the time, map turns records into something to show. Here each player object becomes one line of text for the leaderboard screen. Like forEach, map also hands your callback the item's position, so you can number the lines:

const highScores = [
  { tag: "PixelPanda", score: 8200, level: 7 },
  { tag: "NovaFox", score: 12450, level: 9 },
  { tag: "CaptainCrumb", score: 3100, level: 3 },
];

const lines = highScores.map((player, index) => `${index + 1}. ${player.tag}: ${player.score}`);

lines.forEach((line) => console.log(line));
1. PixelPanda: 8200
2. NovaFox: 12450
3. CaptainCrumb: 3100

Three players went in and three lines came out. map never leaves an item out and never adds one.

Try it. In a file named leaderboard.js, copy the code above and change the callback so each line also says the level, such as 1. PixelPanda: 8200 (level 7). Run it with node leaderboard.js.

The missing return

An arrow function with no curly braces returns its value by itself. Add curly braces, and you have to write return yourself. Forget it, and map still builds a new array, full of undefined:

const scores = [8200, 12450, 3100];

const doubled = scores.map((score) => {
  score * 2;
});

console.log(doubled);
[ undefined, undefined, undefined ]

The callback works out score * 2 and then throws it away, because nothing returns it. Write return score * 2; inside the braces, or drop the braces: (score) => score * 2.

filter: a sieve

Pour pasta into a sieve: the water drains away and the pasta stays. filter is a sieve for arrays. Your callback is the test. It returns true to keep an item and false to let it go, and filter gives back a new array of the items that were kept:

const highScores = [
  { tag: "PixelPanda", score: 8200, level: 7 },
  { tag: "NovaFox", score: 12450, level: 9 },
  { tag: "CaptainCrumb", score: 3100, level: 3 },
  { tag: "TurboSnail", score: 450, level: 1 },
];

const reachedLevelFive = highScores.filter((player) => player.level >= 5);
const overTwenty = highScores.filter((player) => player.score > 20000);

console.log(reachedLevelFive.length);
console.log(overTwenty);
2
[]

Two players reached level 5 or higher, so the new array holds those two whole player objects. Nobody scored over 20,000, so that filter gives back an empty array, [], not an error. A callback that answers true or false like this is sometimes called a predicate in textbooks.

filter and find look alike, but find stops at the first match and gives you that one item. filter checks every item and always gives you an array.

Chaining: filter, then map

At a car wash, the car goes through the soap, then the brushes, then the dryer, and each station passes its result to the next. Because filter gives back an array, you can call map straight on it. That's called method chaining. Writing each step on its own line, starting with a dot, keeps a chain readable:

const highScores = [
  { tag: "PixelPanda", score: 8200, level: 7 },
  { tag: "NovaFox", score: 12450, level: 9 },
  { tag: "CaptainCrumb", score: 3100, level: 3 },
  { tag: "LunaByte", score: 9900, level: 8 },
];

const champions = highScores
  .filter((player) => player.level >= 8)
  .map((player) => player.tag);

console.log(champions);
[ 'NovaFox', 'LunaByte' ]

The order matters. Filter first, while each item still has its level. Map first, and each item would already be a plain tag, with no level left to test.

Try it. Change the chain so it lists the tags of everyone who scored more than 5000 points. You should get three tags.

Check yourself

2 questions to try on the lesson page.

So what

"Show me only these, written like this" is most of what a screen does with data, and filter plus map say it in two short lines. The same chain shapes search results, shopping carts and the leaderboard you'll build at the end of this chapter.

Key terms

Cheat sheets

Related