Topic 03

map, filter, find and sort

JavaScriptHands-on

Most work with a list is one of four jobs: turn every item into something else, keep only some of the items, find one particular item, or put the items in order. You could write a for...of loop for each job, and for years people did. Arrays have a method for each one instead, and each method takes a small function that says how to do its job. That is the first time in this book you hand a function to another function.

Think of an assembly line. Each station takes every item that arrives, does one job to it (reshape it, reject it, pick it out or put it in order) and passes the result along. You design what the station does to one item; the line does the walking along the list.

Handing a Function to a Function

Chapter 6 showed that a function can be stored under a name with const, like any other value. Anything that can be stored can also be passed as an argument, so a function can be handed to another function to use. A function passed in this way is often called a callback, because the method calls it back once for every item.

The examples on this page use the projects array from the previous topic. Keep it at the top of js/main.js, and replace everything below it with each example in turn.

js/main.js
const getTitle = (project) => project.title;

const titles = projects.map(getTitle);
console.log(titles);
Console output
(3) ['Budget Tracker', 'Recipe Cards', 'Weather Now']

getTitle is an arrow function that takes one project and returns its title. The map method calls it three times, once per project, and collects the three answers into a new array, which the Console shows: Budget Tracker, Recipe Cards, Weather Now. Notice that getTitle is passed without round brackets after it. With brackets you would be calling it yourself, right now, instead of handing it over to be called later. Chapter 9 depends on that difference.

map: Every Item, Transformed

map builds a new array of the same length, with each item replaced by whatever the function returns for it. The original array is not touched. The function is usually written right inside the call, with no name of its own.

js/main.js
const urls = projects.map((project) => project.url);

console.log(urls);
console.log(projects[0].title);
Console output
(3) ['https://example.com/budget', 'https://example.com/recipes', 'https://example.com/weather']
Budget Tracker

Read the arrow function as "for a project, give its address". The new array holds the three addresses, in the same order as the projects. The second log shows that the original array still holds whole projects: the first one's title is still Budget Tracker. map made something new and left the original alone.

filter and find

filter keeps some items. Its function is a test: it gets one item and returns true to keep it or false to drop it. The result is always a new array, holding the items that passed, in their original order. It may hold one item, or none. find uses the same kind of test, but stops at the first item that passes and returns that item itself, not an array. When nothing passes, find returns undefined.

js/main.js
const jsProjects = projects.filter((project) => project.tags.includes("javascript"));
const recipes = projects.find((project) => project.title === "Recipe Cards");
const album = projects.find((project) => project.title === "Photo Album");

console.log(jsProjects.length);
console.log(recipes.url);
console.log(album);
Console output
2
https://example.com/recipes
undefined

The filter's test asks whether a project's tags include javascript. Budget Tracker and Weather Now pass, so the new array has 2 items. The first find looks for the project titled Recipe Cards and gets that one object, so reading its address gives the recipes address. The second find looks for a title no project has, and gets undefined. Reading a property of that would be the type error from Chapter 6, which is why the result of find is worth checking before you use it.

sort and Its Surprises

sort puts the items of an array in order, and it has two surprises. First, unlike map and filter, it changes the original array instead of making a new one. Second, unless you tell it how to compare, it turns every item into text and sorts the text, character by character. That is fine for words and wrong for numbers.

Console
> const numbers = [10, 9, 1];
undefined
> numbers.sort()
(3) [1, 10, 9]
> numbers.sort((a, b) => a - b)
(3) [1, 9, 10]
> const scores = [10, 9, 1];
undefined
> scores.toSorted((a, b) => a - b)
(3) [1, 9, 10]
> scores
(3) [10, 9, 1]

Sorted as text, ten comes before nine: text is compared one character at a time, the character 1 comes before the character 9, and the second character is never looked at. So the plain sort gives one, ten, nine. The fix is a compare function: it receives two items, here called a and b, and returns a negative number to put a first, a positive number to put b first, or zero to leave them as they are. Subtracting b from a does exactly that for numbers, and the result is one, nine, ten. Subtract a from b instead to sort from largest to smallest.

When the original order matters, use toSorted, which takes the same compare function but returns a sorted copy and leaves the original alone. The last lines show it: the copy is in order, and scores is still ten, nine, one. toSorted is newer than sort, and has worked in every major browser since 2023; it counts as widely available from 2026.

Chaining: One Step After Another

Because filter returns an array, you can call map on its result straight away, and read the whole thing from left to right, one method per line.

js/main.js
const jsTitles = projects
  .filter((project) => project.tags.includes("javascript"))
  .map((project) => project.title);

console.log(jsTitles);
Console output
(2) ['Budget Tracker', 'Weather Now']

Start with the projects, keep the ones tagged javascript, then keep only their titles: Budget Tracker and Weather Now. The order of the steps matters. Mapping to titles first would throw the tags away, and the filter would have nothing left to test. The filter step is the heart of the project filter the site gets in Chapter 9.

A chain, read left to right
3 projectsthe projects array
→
filtertagged javascript: 2 left
→
maponly the two titles
Common Confusions
  • "map changes the original array." It returns a new array and leaves the original alone. Of these four, sort is the one that changes the original.
  • "filter returns one item." It always returns an array, possibly empty. find is the one that returns a single item, or undefined.
  • "sort understands numbers." Not unless you give it a compare function. Without one, it sorts everything as text.
Why It Matters
  • The site's project filter in Chapter 9 is one filter call.
  • These four methods replace most hand-written loops in real code, and each one says in its name what it is for.

Knowledge Check

What does [1, 2, 3].map((n) => n + 10) give back?

  • The array [11], only the first result
  • The array [11, 12, 13], a new array
  • The number 3, the length of the array
  • The array [1, 2, 3], left unchanged

What is the difference between filter and find?

  • filter changes the array; find only reads it
  • find needs a test function; filter does not
  • filter returns one item; find returns an array
  • filter returns all matches; find returns the first

Why does [10, 9, 1].sort() give [1, 10, 9]?

  • It sorts the numbers as text, character by character
  • It keeps the array in the order it was created
  • It sorts them from the largest to the smallest one
  • It only compares the first item with the last one

After const b = a.toSorted((x, y) => x - y);, what has happened to a?

  • It is now sorted, like b
  • It is now an empty array
  • Nothing; it is still the same
  • It is now in reverse order

You got correct