Blog

Front End Development topics with a touch of humor

Why Map When You Can For?

How I learned to love the map()

September 29, 2020


I remember being distinctly unimpressed when I first was shown the map() function, aka, Array.prototype.map().

What, we need another iteractive method? What does it do that you can't just as easily do with for? Keep in mind, I had just learned my for loops, and it hadn't been that easy. I liked my for loops, and wasn't so excited about learning another way of doing the same thing.

Now, I use map(), whereas my poor for loops have been relegated to just another arrow in the quiver--and not one that's used so much.

For Loops

To review, a for loop is an iterator. I think of it as among the most computery of all methods. It starts at 0, moves on to the next one, reads that, then moves on to the next one (it can be used other ways, but that's how it's usually used). How robotic!

            const arr = [1, 2, 3, 4, 5];

            for (let i = 0; i < 5; i++) {
                console.log(arr[i] * 2);
            }
            
            //2 4 6 8 10
        

There are other, creative ways of using the for loop. I could start at the end and decrement:

            const parr = [1, 2, 3, 4, 5];

            for (let i = 4; i >= 0; i--) {
                console.log(parr[i] * 2);
            }

            // 10 8 6 4 2
        

I could go up by two:

        const parrz = [1, 2, 3, 4, 5];

        for (let i = 0; i < 5; i = i + 2) {
            console.log(parrz[i] * 2);
        }

        // 2 6 8
        

Notice in this case, it starts with 0, then skips to 3, then skips to 5.

Map()

But as useful and computery as this undoubtedly is, for is rather limited. It just plows right ahead mindlessly and executes whatever you put between the curly braces. It doesn't even have an opinion about what you do. In fact, while you can do lots and lots of things with the for loop, it can require some extra lines of code to do those.

For example, say I had an array, and I wanted a new array after having done something to each array item. However, I don't want to mess up the original array. It turns out that this is an extremely common pattern and we will need to do this all the time. Can our old friend for loop do this?

In fact, it can.

            const arr = [1, 2, 3, 4, 5];

            const newArr = [];
            for (let i = 0; i < 5; i++) {
                newArr.push(arr[i]);
            }
            console.log(newArr);
            // [2, 4, 6, 8 , 10]    
        

But compare how map(), the topic of this blog, accomplishes the same thing:

            const arr = [1, 2, 3, 4, 5];

            const newArr = arr.map((item) => item * 2);
            
            console.log(newArr);
            // [2, 4, 6, 8, 10] 
        

Whoah! What just happened? Map() did the same thing in one line instead of three!

How? Well, map() is a more specialized tool than for. If for is one of those Swiss Army knives with lots of blades and a screwdriver and a can opener on it, map() is more like, say, a hammer: good at hammering things and pulling out nails. More specialized.

Map()'s main trait is to take an array, do stuff to each item in turn, and create a new array with the result. Again, it turns out this is a very useful function.

When I began working with React, I saw lots of map() and no for loops at all. React favors a kind of cut-off form of JavaScript and very terse expressions--as opposed to statements. Map gives us that functionality.

Another great thing about map() is that, by its nature, it does not change, or mutate, the array on which it operates. So in our case above, arr remains the same. The creatively named newArr is populated with the results of map()'s work. This principle, of not mutating inputs, is important in React and can be a very useful way of working. If I never change arr, I can use it over and over again, in different contexts, and be assured that I'm not going to create a bug that will mess up my program in other places. Also, if someone works on my code after me, if they use the same principles, they can always rely on arr being the same.

The callback function

Notice that the map method is being passed a function itself--that's called a callback function. The way it works is, the function will be called once with every value of the array. These callback functions can be declared separatedly and called as a variable.

            const arr = [1, 2, 3, 4, 5];
            
            const tripleIt = (num) => num * 3;

            const newArr = arr.map((item) => tripleIt(item));

            console.log(newArr);

            //[3, 6, 9, 12, 15]
        

But you will also see people putting the function right there in the map()'s arguments.

            const arr = [1, 2, 3, 4, 5];

            const newArr = arr.map((item) => item * 3);

            console.log(newArr);

            //[3, 6, 9, 12, 15];
        

Other parameters

Map() has other functionality besides this fundamental one. The first thing is that the callback function has certain expected arguments. So far, we've only been using the first one, currentValue, which represents the elements in the array. In our example, that means 1, 2, 3, 4, 5.

The second argument, which is optional, is index. This gives you access to the index of the array as it is being operated on. You can use this in the same way you use the index in a for loop. For example:

            const arr = [1, 2, 3, 4, 5];

            const newArr = arr.map((item, index) => [item * 2, index]);
            console.log(newArr);

            //[[2,0], [4, 1], [6, 2], [8, 3], [10, 4]];
        

Do you see what we did? We accessed the index of the array, arr, with the second argument of the callback function. I returned an array with that as the second member. It's the most basic thing you can do with an index, but you'll find yourself wanting that index for other things later on.

The callback function has another. The next one is the array on which the function was called (in this case, arr).

The final argument is not for the callback function, but for the map method. It is the this value to use when executing the callback function. Explaining how this works in JavaScript is beyond the scope of the argument, but basically functions work differently depending on their environment (execution context). If you don't write anything, the this will be undefined by map and will default to the normal rules. In some cases, you may need to change the this in order to get the results you want.

Happy mapping!

Image by Mystic Art Design from Pixabay