Coding without loops

Loops are one of the first concepts new programmers learn about. However, they often aren't the most readable way to write code that works with lists.

This article describes what you can use to replace loops in your code.

What's wrong with loops?

Loops make sense when you think about indices and memory locations. But that's what the computer does, not how people think about problems they want to solve.

Humans think about what they want to do with each item. They usually don't care about item number 869 when going through a list - getting whatever is the next item is just fine.

It's odd that we're still using loops so often, given that there are good alternatives.

forEach

Loops are most often used to iterate over a list:

for (var i=0; i<books.length; i++) {
    var book = books[i];
    console.log(book.title);
}

The for loop in this example allows you to look at each book individually and then take an action based on it. In this case, we print the title of the book to the console.

We can rewrite this code using the forEach function that's available on lists in JavaScript. As the name of the function suggests, forEach allows you to take an action for each item in a list.

books.forEach(function(book, i){
    console.log(book.title);
});

A function is used to represent the "action". If you have five books in your list the function is going to be called 5 times, and each time a different book is passed in as the first argument.

By using forEach we can hide the underlying counting logic that the loop is using, and focus on communicating what we want to do.

filter

When working with lists, you often need to search for items that match specific criteria. For example, you could filter a list of books for a particular author.

var booksByJohnResig = [];
for (var i=0; i<books.length; i++) {
     var book = books[i];
     if (book.author === "John Resig") {
         booksByJohnResig.push(book);
    }
}

Based on the existing list of books we generate a new list that only contains books written by John Resig.

JavaScript has a filter function that's made specifically for this purpose. It can significantly shorten our code and make it more self-explanatory:

var booksByJohnResig = books.filter(function(book){
    return book.author === "John Resig";
});

This works by calling the function that's passed into filter for every book in the list, just like forEach does. In this case, the function performs a string comparison with the name of the author we're looking for. If our function returns true the book is included in booksByJohnResig, otherwise it isn't.

You could also go further and use Underscore's filter method to further simplify the code:

var booksByJohnResig = _.filter(books, {author: "John Resig"});

Using Underscore also has the added advantage of providing consistent cross-browser support. The list.filter and list.forEach methods weren't available in Internet Explorer until version 9.

Replacing the loop with a filter function not only shortens our code, it also makes it more expressive.

map

You can use the map function to transform each item in a list into something else.

For example, you could use map to get a list of authors based on the list of books you have:

var authorList = books.map(function(book){
     return book.author;
});

Or generate a list that contains the number of characters in each book title:

var listOfTitleLengths = books.map(function(book){
     return book.title.length;
});

Calling map doesn't modify the original list of books. Instead, it creates a new list and then generates new items for it. The function that's passed into map is called once for each list element and returns the value that should be added to the new list.

Compared to a loop, map shifts the focus of our code from iterating over a list to the transformation that happens to each element.

Write your own loop replacement functions

A loop is very rarely the optimal way to express what your code does.

If there's no native browser function like forEach, filter and map, try to wrap the looping code you write in a helper function. Give your helper function a meaningful name so that it's clear what its purpose is.

For example, you could write this code if you needed to perform an action (i.e. call a function) for a fixed number of times:

function times(howManyTimes, action) {
    for (var i=0; i<howManyTimes; i++){
        action();
    }
}

The times function can then be used to print 5 random numbers, without having to mention loops in the code:

times(5, function(){
    console.log(Math.random());
});

This helper function allows us to say "Print a random number 5 times" instead of "Count from 0 to 4 and print a random number every time".