Skip to content
Theory · Array methods · lesson 2 of 12

map and filter

In the previous lesson the worker at the conveyor only stamped the boxes, and the finished goods went nowhere. Yet one array often has to become another: prices with a discount, or only the positive numbers.

With forEach a new array would have to be built by hand with push, since the method itself returns undefined. Picture a second belt next to the conveyor. The worker takes a box from the first belt and puts what comes out onto the second one. This is how the map method works: it builds the new array itself.

JavaScript
const prices = [10, 25, 40];
const doubled = prices.map((p) => p * 2);

console.log(doubled); // [ 20, 50, 80 ]
console.log(prices); // [ 10, 25, 40 ]

Let's go through the example line by line:

  1. prices is an array of three prices.
  2. prices.map((p) => p * 2) calls the callback for each price and collects the callback's answers into the new array doubled.
  3. The first console.log call prints [ 20, 50, 80 ].
  4. The second prints [ 10, 25, 40 ]: the original array stayed as it was.

map transforms each element

map calls the callback for each element and puts the callback's results into a new array. The new array has as many elements as the original: one result for each element. map does not change the original array itself, so prices stays as it was.

Here the belt analogy limps. In a real factory a box leaves the first belt, while the array prices stays whole after map, with every element in its place.

map fits when each element needs exactly one result: a price gets a discount, a name gets capital letters. The callback gets the element and its index, as in forEach.

The callback can return a value of any type. The call ["ann", "bob"].map((name) => name.toUpperCase()) gives the array of strings ["ANN", "BOB"].

Interviewers often ask how map differs from forEach. The answer is short: map returns a new array of the callback's results, while forEach always returns undefined.

filter keeps a part

Now an inspector stands by the belt. The inspector looks at each box and decides whether to let it through to the second belt or take it off. This is how filter works.

filter also calls the callback for each element, but the callback's result serves as a condition. The new array gets the elements themselves for which the callback returned a truthy value. The rest are dropped, so the new array can be shorter than the original, or empty.

The callback need not return exactly true or false: its result is checked like the condition of an if.

JavaScript
const nums = [4, 7, 10, 3];

console.log(nums.filter((n) => n > 5)); // [ 7, 10 ]
console.log(nums.filter((n) => n > 100)); // []

Let's go through both calls:

  • the condition n > 5 holds for 7 and 10, and filter returns [ 7, 10 ];
  • the condition n > 100 holds for no number, and the result is the empty array [].

filter does not change the original array either: nums still holds four numbers.

Chaining calls

map and filter return an array, so the next method can be called on the result right away. This form is called method chaining. The methods run from left to right, and each works on the array the previous one returned:

JavaScript
const scores = [55, 90, 72, 40];
const raised = scores.filter((s) => s >= 60).map((s) => s + 5);

console.log(raised.join(" ")); // 95 77

A chain is like two stations on one conveyor. Let's pass them in order:

  1. filter lets through only the scores from 60 up, which gives [90, 72].
  2. map adds 5 to these two numbers only and returns [95, 77].
  3. join(" ") glues them into the string 95 77.

The order of the stations matters. If the methods swap places, filter checks the numbers that were already raised.

Common mistake

The goal is to keep the even numbers, but the condition holds only the remainder:

JavaScript
const nums = [1, 2, 3, 4];
const evens = nums.filter((n) => n % 2); // ✗

console.log(evens);

There is no error, but the program prints [ 1, 3 ]. An even number has the remainder 0, a falsy value, and filter drops such an element. An odd number has the remainder 1, a truthy value, and the element stays. Compare the remainder with zero: nums.filter((n) => n % 2 === 0) gives [ 2, 4 ].

Key points

  • map returns a new array of the same length made of the callback's results.
  • filter returns a new array of the elements for which the callback returned a truthy value.
  • Neither method changes the original array, and both return a new one, so their calls are chained.
  • Unlike map, forEach always returns undefined.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way