loading...
Cover image for Refactoring every .forEach to a .map

Refactoring every .forEach to a .map

dmahely profile image Doaa Mahely ・3 min read

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"

.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]

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;
});
let doubled = arr.map(num => {
    return num * 2;
});

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",
    },
    ...
    ]
},
...
]

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;

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",
    },
    ]
},
...
]

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;

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.

Posted on by:

dmahely profile

Doaa Mahely

@dmahely

Software engineer and full stack developer. She/her.

Discussion

pic
Editor guide
 
 

Thanks Gbenga 😄