DEV Community

Cover image for Refactoring every .forEach to a .map
Doaa Mahely
Doaa Mahely

Posted on

Refactoring every .forEach to a .map

I often read JavaScript articles and noticed that many of them focus heavily on map, filter and reduce, more so than other array methods. In my current React project, I noticed that I've used map more but also that I unconsciously used forEach a bunch of times, so I decided to dig into it more.

First, let's go over what we know about the two functions.

.forEach

  • Goes over the array it is called on and executes the callback function on each element
  • Changes the original array
  • Returns undefined
// MDN's example

const array1 = ['a', 'b', 'c'];

array1.forEach(element => console.log(element));

// expected output: "a"
// expected output: "b"
// expected output: "c"
Enter fullscreen mode Exit fullscreen mode

.map

  • Goes over the array it is called on and executes the callback function on each element
  • Does not change the original array
  • Returns a new array with the result
// MDN's example

const array1 = [1, 4, 9, 16];

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(map1);
// expected output: Array [2, 8, 18, 32]
Enter fullscreen mode Exit fullscreen mode

As we saw, both functions do the exact same thing, and both are supported in all browsers. However, the way they work under the hood differs.

Differences

1. Use cases

map returns a new array without mutating the original, so it makes sense to use it if we are planning to use the returned array elsewhere. However, if we don't mind mutating the original array and aren't planning to use it somewhere else, using forEach is recommended. Per MDN:

You shouldn't be using map if you're not using the array it returns and/or you're not returning a value from the callback.

2. Approach

Because of map’s nature, people who prefer a more functional approach will lean towards it. Functional programming is an approach to building software by using pure functions that take an input and return an output. In functional programming, functions do not modify variables outside of their scope, state is not shared and data is never mutated. This approach greatly diminishes the number of bugs that may be introduced into your application.

3. Performance

According to this post, map seems to be much more performant than forEach. However, I've since learned there's a big deal of controversy around this particular benchmark. It's not fair to compare the following two pieces of code

arr.forEach((num, index) => {
    return arr[index] = num * 2;
});
Enter fullscreen mode Exit fullscreen mode
let doubled = arr.map(num => {
    return num * 2;
});
Enter fullscreen mode Exit fullscreen mode

since the return statement in the first example serves no purpose. Moreover, the performance of these functions depends on the implementation of JavaScript engines in different browsers, so the comparison is not so cut-and-dried.

4. Asynchronous programming

As stated by MDN, forEach expects its callback to be synchronous, so it won't play well with any fancy async code you may have. map however will be more tolerant.

Refactoring

In my project, I was using forEach to iterate over my rounds array, which looks like this:

"rounds": [
{
    "artists": [
    {
        "name": "Three Days Grace",
    },
    {
        "name": "IDK",
    },
    ...
    ]
},
...
]
Enter fullscreen mode Exit fullscreen mode

I have an array of strings that contains images of the current round's artists, and I wanted to iterate over each artist and add the image property to the object. This is how I first achieved that:

const artistImages = getArtistData(artistData); // returns an array of strings
rounds.forEach((round, index) => {
   round.artists[currentRound].image = artistImages[index];
});
return rounds;
Enter fullscreen mode Exit fullscreen mode

I was calling this function in a component then assigning the returned array in state. Now, the state looked like this:

"rounds": [
{
    ...
    "artists": [
    {
        "name": "Three Days Grace",
        "image": "https://i.scdn.co/image/2a9ec8d494f8d0a52fd67c3239efc2b9e79a4ced"
    },
    {
        "name": "IDK",
        "image": "https://i.scdn.co/image/6594334f1d1a2e65394d27eb457ccff203886411",
    },
    ]
},
...
]
Enter fullscreen mode Exit fullscreen mode

Refactoring to use map was quite simple and achieved the same result. It is exactly the same line of code, but now I'm returning something inside map and saving it in a variable, then returning that variable to my component.

const artistImages = getArtistData(artistData); // returns an array of strings
const roundsWithImages = rounds.map((round, index) => { 
   round.artists[currentRound].image = artistImages[index];
   return round;
});

return roundsWithImages;
Enter fullscreen mode Exit fullscreen mode

And now hopefully my code is a little more readable and the likelihood of me messing up due to mutated variables is a little smaller!

Thank you for reading. Let me know how I can make this article better. Until next time 👋

Cover photo by Oliver Hale on Unsplash.

Oldest comments (2)

Collapse
 
gbenga profile image
Gbenga Ojo-Aromokudu

Really enjoyed!

Collapse
 
dmahely profile image
Doaa Mahely

Thanks Gbenga 😄