Loops make me dizzy. I would rather write my code using the map function. It’s similar to a foreach loop, but with a twist. Instead of traversing a collection, map transforms a collection. I only realized this similarity, however, after writing many loops where I should have used map.

Today, this is how I decide which to use:

  • Map function — use when transforming a collection
  • Foreach loop — use when traversing a collection
  • For loop — only use when I don’t have a collection

Transforming Collections

Collection is a general term — it’s any variable that contains elements. For example, my collection of selfies that I took at an art museum. I want to transform my selfies into black-and-white photos before uploading them to my social feed.

3 selfies I took at an art musuem
Grayscale versions of my selfies.

Foreach Loop

Assume I have a pure function called grayscale that takes an image and returns a grayscale version of that image.

const selfies = [selfieA, selfieB, selfieC];
const coolSelfies = [];

// ES6 JavaScript foreach loop syntax
for (const selfie of selfies) {  
   coolSelfies.push(grayscale(selfie));
}

This code works fine but has a readability issue. A future maintainer of the code must spot the push statement to understand the relation between selfies and coolSelfies. This may seem like a small gripe. But imagine if that statement was buried under more lines of code. The relationships between collections can become confusing.

Map() Function

I should have written the loop using the JavaScript array map function.

const selfies = [selfieA, selfieB, selfieC];

const coolSelfies = selfies.map(selfie => {
   return grayscale(selfie); 
});

This code highlights the relationship between the two collections — coolSelfies is declared as a transformed version of selfies. Transforming a collection means looping through a collection for the purpose of building a new collection. Using map clarifies this intent to readers.

Code Clarity

In JavaScript, the arrow function syntax allows an implied return statement if I leave out the curly brackets.

const coolSelfies = selfies.map(selfie => grayscale(selfie));

Depending on how grayscale is defined, I can make this code even shorter by removing the anonymous arrow function.

const coolSelfies = selfies.map(grayscale);

I can read this code easier than a loop. But not all loops should be avoided. Loops that don’t transform a collection should stay as foreach loops.

Traversing Collections

Traversing means iterating or looping through a collection. For example, I want to post each of my cool selfies to my social media feed. Assume I have a function upload that takes an image and uploads it to the internet.

for (const selfie of coolSelfies) {
  upload(selfie);
}

An alternative choice would be using the JavaScript array forEach function.

coolSelfies.forEach(selfie => upload(selfie));

This is a case where a foreach loop is better than a map function, because of several reasons:

  • upload has no return value, so there is no way to create a new collection from the loop.
  • upload isn’t a pure function. It’s best practice to not use map with functions that have side-effects, such as starting an upload.

Code Clarity

At this point, I must contradict myself. Loops are often more readable than map. Programmers understand loops. Loops are common. Loops exist in almost every programming language.

The map function is not as widespread. It involves functional programming. Functional programming is not how most people learn to code, regardless of being trained or self-taught.

I’ve refactored many foreach loops into map functions thinking I was making the code more readable. I try not to do that anymore. It’s not worthwhile to tear up vetted code just to avoid loops.

Avoid For Loops

A less drastic approach is to avoid writing for loops. Here’s my original loop as a for loop:

const selfies = [selfieA, selfieB, selfieC];
const coolSelfies = [];

for (const index = 0; index < selfies.length - 1; index++) {
   coolSelfies.push(grayscale(selfies[index]));
});

The for loop signature (the length conditional and the index variable) takes up a lot of space in my code. But the size of the loop and the direction of the loop aren’t actually that important. A foreach loop would be better here because it abstracts away those variables.

I still use for loops, but only for tasks that a foreach loop couldn’t accomplish. Such as precise array manipulation or looping a constant number of times.

Using the right loop for the job

Any type of loop will get the job done. But I like to use the most specific type of loop that I possibly can. This clarifies the code and prevents bugs.

  • For loop — general looping
  • Foreach loop — looping through a collection
  • Map function — transforming a collection into another collection

If you want to learn more about using map in your code, read up on functional programming. Functional programming is all about combining map with other transformative functions, such as filter and reduce.

One Comment

  • Phil Parker says:

    I’ve been out of the loop of language development for so long that neither foreach nor map are familiar concepts. foreach is so obvious and understandable that I got it instantly from seeing one example. Further, its benefits are immediately obvious. If I refactor my application to use a binary tree for a collection instead of an array, I don’t have to rewrite all my loops!
    map, on the other hand, will take some brain rewiring. I haven’t done any sort of functional programming since LISP in college and that has faded, and I daresay that I actually got the hang of LISP more than any of my classmates back then.

    I’d suggest that the interest of your code being easily understandable to anyone who might need to see it would recommend using foreach until you have an application that significantly benefits from map, where map expesses something fundamentally different than a simple loop, rather than just expressing the same thing in an arguably better way.

Leave a Reply