Skip to main content

Someone wanted to learn this too, so Grasp built them a personal learning path.

You can keep exploring this one or create your own

Transforming Arrays with Iteration, Map, Filter, and Reduce

Hello! Last lesson, you used a loop to pass one value at a time into a function. Now we’ll use that same idea to work with whole arrays: visit each value, transform values, keep selected values, and combine values into a result. These operations appear often in JavaScript and Node.js code that prepares data for an API or report.

Allow about 40 minutes, including time to run the examples. By the end, you should be able to explain not just which method to use, but what data comes out of each step.


Start with a loop you can trace

Imagine an array containing the number of minutes five support tickets have waited. A zero means the ticket has not waited:

const waitMinutes = [5, 0, 12, 0, 8];

const fromLoop = [];

for (const minutes of waitMinutes) {
  if (minutes > 0) {
    fromLoop.push(minutes);
  }
}

console.log(fromLoop);    // [5, 12, 8]
console.log(waitMinutes); // [5, 0, 12, 0, 8]

The for...of loop visits each number. The if decides whether it belongs in fromLoop; push adds it to that result array. We have changed the new array, not the input.

Array methods package common versions of this pattern. You give a method a callback function—a function it calls as it visits elements. An arrow function such as (minutes) => minutes > 0 is a compact way to write one: minutes is its parameter, and the expression after => is its returned value.

Watch these short demonstrations from freeCodeCamp.org’s Array Iteration: 8 Methods. Follow what each callback returns and whether the method produces an array or one combined value.

Array Iteration: 8 Methods - map, filter, reduce, some, every, find, findIndex, forEach

In Array Iteration: 8 Methods, freeCodeCamp.org demonstrates the four methods we will use, starting with simple numeric arrays.

Watch forEach for visiting and printing, map for transforming each number, filter for keeping matches, and reduce for carrying a running result. In the final segment, notice that the accumulator is the callback's first parameter and the current item is its second.

If your aim is simply to perform an action for each value, forEach is another way to iterate:

waitMinutes.forEach((minutes, index) => {
  console.log(`Ticket ${index + 1}: ${minutes} min`);
});

Here index is the position, starting at zero. forEach ignores values returned by its callback, and the forEach call itself returns undefined. It is suitable for an action such as logging, but not for building a returned array. For that, use a loop or one of the methods below.


Use map when every element gets a result

Suppose a report needs seconds rather than minutes:

const waitSeconds = waitMinutes.map((minutes) => minutes * 60);

console.log(waitSeconds); // [300, 0, 720, 0, 480]
console.log(waitMinutes); // [5, 0, 12, 0, 8]

map calls the callback for each input element and places its returned value in a new array. It preserves the number and order of elements: five minutes become 300 seconds; zero minutes become zero seconds. waitMinutes remains unchanged.

That emphasis on return matters. If you use braces for a longer callback, write return explicitly:

const labels = waitMinutes.map((minutes) => {
  return `${minutes} minutes waiting`;
});

console.log(labels);
// ["5 minutes waiting", "0 minutes waiting", ...]

Without return inside those braces, each position in labels would contain undefined. Think back to the last lesson: printing something inside a function is not the same as returning it to the caller.


Use filter when some elements should be kept

Now produce the same values that our first loop collected:

const activeWaits = waitMinutes.filter((minutes) => minutes > 0);

console.log(activeWaits); // [5, 12, 8]

For each number, the callback answers a yes-or-no question. When it returns true, filter keeps that original value; when it returns false, filter leaves it out. The result is a new array whose length can be smaller—even zero if nothing matches. The retained elements stay in their original order.

This distinguishes the two methods:

  • map asks, “What value should go in this position?” Its result has one entry per input entry.
  • filter asks, “Should this value be included?” Its result can have fewer entries.

You can use them together. First keep tickets that have waited, then express those waits in seconds:

const activeSeconds = waitMinutes
  .filter((minutes) => minutes > 0)
  .map((minutes) => minutes * 60);

console.log(activeSeconds); // [300, 720, 480]

Read this in stages. After filter, the data is [5, 12, 8] in minutes. After map, it is [300, 720, 480] in seconds. Naming the intermediate arrays separately is equally valid when a chain is difficult to follow.

map and filter create new arrays rather than changing the input array themselves. Later, when you work with arrays of objects, remember that this does not automatically copy each object inside them; we’ll examine objects in the next lesson.


Use reduce to carry a result across the array

A report might also need the total waiting time. With a loop, you could start a total at zero and add each number. reduce expresses that same running calculation:

const totalMinutes = activeWaits.reduce(
  (total, minutes) => total + minutes,
  0
);

console.log(totalMinutes); // 25

Unlike map or filter, this callback has two essential inputs in this order: the accumulated total so far, then the current minutes. The 0 after the callback is the initial total. Each returned sum becomes the total supplied to the next call:

Current total Current minutes Returned total
0 5 5
5 12 17
17 8 25

So reduce returns 25, a single number, rather than an array. Its callback must return the next accumulated value; forgetting that return breaks the running calculation.

Read JavaScript.info’s explanation to reinforce this step-by-step model. Its examples use browser alert(); our Node.js examples use console.log() instead.

Array methods

Read the “reduce/reduceRight” subsection of JavaScript.info’s Array methods for its sum walkthrough and explanation of the initial value.

In “reduce/reduceRight,” just after the split and join examples, begin where the author says “When we need to iterate over an array.” Read the walkthrough through the empty-array example, stopping before the discussion of reduceRight. Focus on how each returned sum becomes the next call's accumulator, and why providing an initial value makes an empty-array result well defined.

The initial value is especially useful when no elements pass a filter:

const noWaits = waitMinutes.filter((minutes) => minutes > 100);
const total = noWaits.reduce((sum, minutes) => sum + minutes, 0);

console.log(noWaits); // []
console.log(total);   // 0

With an initial 0, reducing an empty array returns 0. Without an initial value, reducing an empty array throws an error. Choose an initial value that makes sense for the result you are building.


Explain a complete transformation

The image below shows the three methods acting on one numeric array. Each intermediate result matters: doubling the numbers produces a new array, filtering keeps only numbers greater than ten, and reducing sums those remaining numbers.

The array `[3, 6, 9, 12]` is doubled with `map` to `[6, 12, 18, 24]`, narrowed with `filter` to `[12, 18, 24]`, then summed with `reduce` to produce `54`.

Here is the same pattern applied to our ticket waits. Save it as arrays.js and run node arrays.js:

const waits = [5, 0, 12, 0, 8];

const waitingSeconds = waits
  .filter((minutes) => minutes > 0)
  .map((minutes) => minutes * 60);

const totalWaitingSeconds = waitingSeconds.reduce(
  (sum, seconds) => sum + seconds,
  0
);

console.log(waitingSeconds);      // [300, 720, 480]
console.log(totalWaitingSeconds); // 1500
console.log(waits);               // [5, 0, 12, 0, 8]

There are three different shapes of data here: waits is the original five-element array, waitingSeconds is a three-element array, and totalWaitingSeconds is one number. Being able to state those shapes—and the units of their values—is a useful way to check a transformation before trusting its output.


Takeaways

Use for...of when an explicit loop makes the work clearest, and forEach when you want to perform an action for each element without using a returned array. Use map to return one transformed value per element, filter to return the elements that pass a test, and reduce to carry an accumulated result. With reduce, track the initial value and what the callback returns at every step.

Next, we’ll work with JavaScript objects—the kind of structured ticket data these arrays will often contain.

Can't find a good explanation? Sign up and we'll make it for you

Sign up