We use cookies to enhance your experience on the site
CodeWorlds

Array Iteration (forEach, map, filter, reduce)

In our Jurassic Park we collect a mountain of data about the dinosaurs - their species, measurements, feeding habits, aggression levels and much more. Once all of that lands in an array, we hardly ever care about a single entry: we want to visit every element, reshape them, pull out the ones that matter, or boil the whole herd down to one number for the control room. JavaScript ships a family of array iteration methods built for exactly that, and each of them has a job of its own.

The forEach Method

The

forEach
method runs a function once for every element of an array. It is the most basic way of iterating - we reach for it when we want to do something with each element, such as printing it or sending it to the monitoring station, and we do not need a new array in return.

1// An array of objects representing the dinosaurs in our park
2const dinosaurs = [
3  { name: "Tyrannosaurus", diet: "carnivore", aggressionLevel: 10 },
4  { name: "Triceratops", diet: "herbivore", aggressionLevel: 4 },
5  { name: "Velociraptor", diet: "carnivore", aggressionLevel: 9 },
6  { name: "Stegosaurus", diet: "herbivore", aggressionLevel: 2 },
7  { name: "Brachiosaurus", diet: "herbivore", aggressionLevel: 1 }
8];
9
10// We use forEach to print the names of all the dinosaurs
11console.log("List of dinosaurs in the park:");
12dinosaurs.forEach(function(dino) {
13  console.log(dino.name);
14});
15
16// We can also use an arrow function for a more concise notation
17console.log("List of dinosaurs with their aggression level:");
18dinosaurs.forEach(dino => {
19  console.log(`${dino.name}: Aggression level ${dino.aggressionLevel}/10`);
20});

The

forEach
method hands nothing back - to be exact, it returns
undefined
- so we use it when we only want to perform an action on the elements, without building a new array. It is worth knowing what it cannot do either: you cannot stop it halfway through. A
return
inside the callback ends that one call and the walk moves straight on to the next dinosaur, and
break
is not even allowed in there. When you need to bail out early, that is a job for
find
,
some
,
every
or a classic
for
loop.

The map Method

The

map
method creates a new array containing the results of calling a provided function on every element of the original array. It is remarkably useful whenever we need to convert data from one shape into another - turning full dinosaur records into a plain list of names, or into short safety summaries the wardens can actually read.

1// We use map to build a new array containing only the dinosaur names
2const dinoNames = dinosaurs.map(dino => dino.name);
3console.log("Dinosaur names:", dinoNames);
4// Result: ["Tyrannosaurus", "Triceratops", "Velociraptor", "Stegosaurus", "Brachiosaurus"]
5
6// We can also build more complex transformations
7const dinoSummaries = dinosaurs.map(dino => {
8  return {
9    name: dino.name,
10    dangerLevel: dino.diet === "carnivore" ? "High" : "Low",
11    warning: dino.aggressionLevel > 7 ? "Handle with extreme caution!" : "Standard safety procedures"
12  };
13});
14
15console.log("Dinosaur safety summary:", dinoSummaries);

Unlike

forEach
, the
map
method always returns a new array of the same length as the original, holding the transformed elements. That is exactly the key difference between the two:
map()
returns a new array, while
forEach()
returns
undefined
. It is not a question of speed -
map()
is not faster, both methods visit every element exactly once - and they certainly do not work identically. Neither of them can be stopped mid-run, so that is no difference between them either. Reach for
map
when you want the result, and for
forEach
when you only care about the side effect.

The filter Method

The

filter
method creates a new array containing only the elements that satisfy a given condition. It is the perfect tool when we need to pull a subset out of a bigger collection - every carnivore, every animal due for a health check, every enclosure in the eastern sector.

1// We filter to get only the carnivorous dinosaurs
2const carnivores = dinosaurs.filter(dino => dino.diet === "carnivore");
3console.log("Carnivorous dinosaurs:", carnivores);
4
5// We can combine conditions to build more complex filters
6const dangerousDinos = dinosaurs.filter(dino => {
7  return dino.diet === "carnivore" && dino.aggressionLevel > 7;
8});
9console.log("The most dangerous dinosaurs:", dangerousDinos);
10
11// We can also combine filter with map, to narrow down first and transform afterwards
12const safetyWarnings = dinosaurs
13  .filter(dino => dino.aggressionLevel > 5)
14  .map(dino => `WARNING: ${dino.name} has a high aggression level!`);
15
16console.log("Safety warnings:", safetyWarnings);

The

filter
method returns a new array which may be shorter than the original, holding only the elements that made it through the "filter" - the ones for which the test function returned
true
. Notice that the
dinosaurs
array itself is left completely untouched: just like
map
,
filter
never edits the collection it was called on, it only reports on it.

The reduce Method

The

reduce
method is the most powerful of the iteration methods, and also the most involved. It lets us "reduce" an array to a single value by running a function over every element while carrying the result of the previous step along with us.

1// We calculate the sum of the aggression levels of all the dinosaurs
2const totalAggressionLevel = dinosaurs.reduce((total, dino) => {
3  return total + dino.aggressionLevel;
4}, 0); // 0 is the initial value
5
6console.log("Total aggression level in the park:", totalAggressionLevel);
7
8// We can use reduce to build richer structures, such as an object grouping dinosaurs by diet
9const dinosByDiet = dinosaurs.reduce((groups, dino) => {
10  // If the group for this diet does not exist yet, we create it
11  if (!groups[dino.diet]) {
12    groups[dino.diet] = [];
13  }
14
15  // We add the dinosaur to the matching group
16  groups[dino.diet].push(dino.name);
17
18  return groups;
19}, {}); // {} is the initial value (an empty object)
20
21console.log("Dinosaurs grouped by diet:", dinosByDiet);
22// Result: { "carnivore": ["Tyrannosaurus", "Velociraptor"], "herbivore": ["Triceratops", "Stegosaurus", "Brachiosaurus"] }

So what is the

reduce()
method on an array used for? It reduces an array to a single value - a sum, an average, the heaviest specimen, or one grouped object like the one above. The name misleads a lot of people, so let us be precise about what it does not do: it does not remove elements from an array, it does not reduce the size of the array (our
dinosaurs
array still holds five records afterwards), and it has nothing to do with sorting in descending order - that job belongs to
sort
.

The

reduce
method takes two parameters:

  1. A reducer function, which itself takes at least two parameters:
    • The accumulator: the value returned from the previous iteration (or the initial value)
    • The current element: the element of the array being processed right now
  2. An initial value (optional, but strongly recommended)

The find Method

The

find
method returns the first element of an array that satisfies a given condition. When the park systems need one specific record rather than a whole list, this is the method to reach for.

1// We look for a dinosaur with a given name
2const stegosaurus = dinosaurs.find(dino => dino.name === "Stegosaurus");
3console.log("Stegosaurus information:", stegosaurus);
4
5// We look for the first carnivore with an aggression level above 8
6const dangerousCarnivore = dinosaurs.find(dino => {
7  return dino.diet === "carnivore" && dino.aggressionLevel > 8;
8});
9console.log("First dangerous carnivore:", dangerousCarnivore); // Tyrannosaurus

Unlike

filter
, which returns an array of every matching element,
find
returns only the first match, and when nothing matches it returns
undefined
. It also stops the moment it gets a hit, so the remaining dinosaurs are never examined. Its close twin
findIndex
behaves the same way but gives you the position of that first match instead of the element itself, and returns
-1
when there is no match at all.

The some and every Methods

The

some
and
every
methods return a boolean value and are useful for checking whether the elements of an array meet certain criteria. Neither of them builds a new array - they answer a yes-or-no question about the whole collection.

some

The

some
method checks whether at least one element of the array satisfies the given condition.

1// We check whether there are any dangerous dinosaurs in the park
2const hasHighAggressionDinos = dinosaurs.some(dino => dino.aggressionLevel > 8);
3console.log("Does the park have high aggression dinosaurs?", hasHighAggressionDinos); // true
4
5// We check whether there are any omnivorous dinosaurs in the park
6const hasOmnivores = dinosaurs.some(dino => dino.diet === "omnivore");
7console.log("Does the park have omnivorous dinosaurs?", hasOmnivores); // false

The

some
method short-circuits: it stops as soon as it finds the first element that passes the test, because it already knows the answer is
true
and looking at the rest of the herd would change nothing.

every

The

every
method checks whether all the elements of the array satisfy the given condition.

1// We check whether every dinosaur has an aggression level assigned
2const allHaveAggressionLevel = dinosaurs.every(dino => dino.aggressionLevel !== undefined);
3console.log("Do all the dinosaurs have an aggression level?", allHaveAggressionLevel); // true
4
5// We check whether all the dinosaurs are herbivores
6const allHerbivores = dinosaurs.every(dino => dino.diet === "herbivore");
7console.log("Are all the dinosaurs herbivores?", allHerbivores); // false

The

every
method short-circuits the other way round: it stops at the first element that fails the test, because a single counterexample is enough to answer
false
. Read out loud, the pair sounds almost like plain English - "is there some dangerous dinosaur?" and "is every dinosaur fed?".

The sort Method

The

sort
method orders the elements of an array and returns the sorted array. By default it compares elements as strings, which can lead to unexpected results when sorting numbers - by default
[10, 9, 100]
comes out as
[10, 100, 9]
. That is why we usually pass it a comparison function.

1// We sort the dinosaurs by name (alphabetically)
2const dinosSortedByName = [...dinosaurs].sort((a, b) => {
3  return a.name.localeCompare(b.name);
4});
5console.log("Dinosaurs sorted alphabetically:", dinosSortedByName);
6
7// We sort the dinosaurs by aggression level (from the least to the most aggressive)
8const dinosSortedByAggression = [...dinosaurs].sort((a, b) => {
9  return a.aggressionLevel - b.aggressionLevel;
10});
11console.log("Dinosaurs sorted by aggression (ascending):", dinosSortedByAggression);
12
13// We sort the dinosaurs by aggression level (from the most to the least aggressive)
14const mostDangerousFirst = [...dinosaurs].sort((a, b) => {
15  return b.aggressionLevel - a.aggressionLevel;
16});
17console.log("Dinosaurs sorted by aggression (descending):", mostDangerousFirst);

Notice that we use the spread operator (

...
) to create a copy of the array before sorting, because
sort
modifies the original array and we want to preserve the original order. The comparison function decides the direction: a negative result puts
a
first, which is why
a.aggressionLevel - b.aggressionLevel
sorts ascending, and flipping the two operands sorts descending.

Combining Methods (Chaining)

One of the most powerful features of these methods is that we can join them into "chains" and carry out complex operations on our data in a single readable expression. It works because

filter
,
map
and
sort
all give back an array, so the next method can be called straight on the result.

1// Let us find the names of all the herbivorous dinosaurs, sorted alphabetically
2const sortedHerbivoreNames = dinosaurs
3  .filter(dino => dino.diet === "herbivore")  // First we filter
4  .map(dino => dino.name)                     // Then we transform into an array of names
5  .sort();                                   // Finally we sort alphabetically
6
7console.log("Herbivores (alphabetically):", sortedHerbivoreNames);
8// Result: ["Brachiosaurus", "Stegosaurus", "Triceratops"]
9
10// Let us calculate the average aggression level of the carnivores
11const averageCarnivoreAggression = dinosaurs
12  .filter(dino => dino.diet === "carnivore")   // First we keep only the carnivores
13  .reduce((sum, dino, index, array) => {       // Then we reduce it down to an average
14    sum += dino.aggressionLevel;               // We add the aggression level to the sum
15
16    // If this is the last element, we divide by the number of elements
17    if (index === array.length - 1) {
18      return sum / array.length;
19    }
20
21    return sum;                               // We return the partial sum
22  }, 0);
23
24console.log("Average carnivore aggression level:", averageCarnivoreAggression); // 9.5

Read a chain from left to right and the order of its links is always the same: the array comes first, then the method that narrows it down, then the method that transforms whatever survived. The array is the receiver, so

dinosaurs
always opens the chain, then comes
.filter(...)
, and only then
.map(...)
.

1// If every record carried a ready dangerous flag, the chain would read like a sentence
2const dangerousNames = dinosaurs
3  .filter(d => d.dangerous)
4  .map(d => d.name);
5
6// Our records store a diet string instead, so the test is a little longer -
7// but the order of the links in the chain never changes
8const carnivoreNames = dinosaurs
9  .filter(d => d.diet === "carnivore")
10  .map(d => d.name);
11
12console.log("Carnivore names:", carnivoreNames);

Swapping those two links around would break everything: after

.map(d => d.name)
we are holding an array of plain strings, and a string has no
dangerous
property left to filter on. Filter first, map second - that is the order worth memorising.

Summary

The array iteration methods in JavaScript are an invaluable tool when working with collections of data. Each of them has its own speciality:

  • forEach: when we want to perform an operation on every element, without creating a new array
  • map: when we want to transform every element of the array into something else
  • filter: when we want to select the subgroup of elements that meet given criteria
  • reduce: when we want to "reduce" the array to a single value or a new structure
  • find: when we are looking for the first element that meets a given criterion
  • some/every: when we want to check whether some/all elements meet a given criterion
  • sort: when we want to put the elements of the array in order

In our Jurassic Park these methods are indispensable for managing dinosaur data efficiently, monitoring safety and making decisions based on data. As Dr. Rex puts it, iteration methods are the park's morning briefing:

forEach
walks the daily checklist,
filter
picks out only the animals that need attention,
map
turns them into a report, and
reduce
gives the control room a single number to act on.

Go to CodeWorlds