2012.12.03

Why I favor while loops

I remember that my first contact with programming loops was through the for statement and I used it almost exclusively for many years. I’ll explain why I’ve been favoring while loops over for loops in the past couple years. Beware that this is only my personal preference and the reasoning behind it are very subjective. Use what works better for you.

TL;DR: while is less verbose and more clear in many cases.

Less tokens per line

Lets take a very basic example into consideration:

for(var i = 0; i < 100; i++) {
    console.log(i);
}

Converted into a while loop:

var i = -1;
while(++i < 100) {
    console.log(i);
}

What bothers me more on the for statement is that you have too many things going on the same line. Variable declaration, conditional logic and another expression to increment the variable. I find the line while(++i < 100) way easier to read/understand than for(var i = 0; i < 100; i++), less tokens to grok and a single responsibility.

Another benefit that I see by moving the var declaration to outside the loop logic is that it makes it clear that variables aren’t bound to the loop scope (JavaScript only have block scope if you use the let keyword which isn’t standard). If you are enforcing manual variable hoisting and/or single var statement on your code guidelines the while loop will make even more sense.

Many operations can be executed in reverse order

Many types of operations can be executed in reverse order without affecting the end result. while loops are very succinct and clear when looping backwards:

var n = 100;
while (n--) {
    console.log(n);
}

Compared to a for loop that does the same:

for(var n = 99; n >= 0; n--) {
    console.log(n);
}

Notice that on the for loop you need to subtract 1 from the length to achieve same result (array indexes starts at zero).

PS: Some operations are slightly faster when executed in reverse since they avoid changing the size of the Array during the operations. (change it only once upfront)

Processing Array elements

It is very common to use for/while loops to process array items. It’s a common knowledge that we should avoid property lookups whenever possible to increase performance, so it’s a very common practice to cache the Array.length. By doing that the for declaration gets even more complex:

for(var i = 0, n = arr.length; i < n; i++) {
    console.log(arr[i]);
}

I would rather write it like this:

var i = -1,
    n = arr.length;
while (++i < n) {
    console.log(arr[i]);
}

Each line takes care of a single responsibility, less lines of code isn’t always a good thing.

If you are 100% sure that the array won’t contain falsy values (null, undefined, 0, false, "") you could make it even simpler:

/* jshint boss:true */
var cur, i = 0;
while (cur = arr[i++]) {
    console.log(cur);
}

Beware that falsy values will break the loop, use with care.

PS: I’ve been favoring Array#forEach, Array#map, Array#filter in many cases since I think it focus the program code on the actual problem and not the loop logic, but I still use plain loops in some cases to iterate over arrays.

RegExp.exec

The same pattern above could be used for methods like RegExp#exec:

var re = /\w+/g,
    match;
while (match = re.exec('== Lorem ipsum dolor sit amet ==')) {
    console.log(match);
}

This will execute the RegExp#exec method over the whole string until it can’t find anything that matches the pattern. It can simplify parsing logic a lot.

Getting “clever”

I’ve seen lots of people abusing the fact that you can omit the expressions on the for loop or that you can use the comma operator to do tricky things, but why not use the while statement instead?

// first and last expressions are optional
var i = 0, cur;
for( ; cur = arr[i++]; ) {
    // ...
}

By (ab)using the comma operator you can basically remove all the work from the while body and move it to the expression block.

// will execute until `cur` is falsy and/or `cur.doSomething()` retuns
// a falsy value
var i = 0, cur;
while( cur = arr[i++], cur && cur.doSomething() );

You can find a real working example of this concept applied to a very small snippet on the IE version detection script by padolsey and friends. I also use something similar on js-signals to execute all bindings of a signal until one of them stops the propagation (without the comma operator).

I’m not a fan of very clever tricks since I think they usually makes code harder to understand, but it is very good to know when/how to use these techniques since they can open your mind to simpler solutions in some cases.

Bonus: do-while

do-while is one of these features that I rarely need, but in some cases it can be useful. On a recent project I needed a greedy algorithm to search for the next empty cell on a grid that wasn’t adjacent to a similar item (couldn’t place product bottles next to each other before user starts filtering the grid items), the easiest way to solve it was to write a do-while:

do {
    // get next empty cell that can fit item
    cellIdx = this._getFitIndex(item, rowIdx, cellIdx);
    // check if cell isn't adjacent to a similar item (on current & prev rows)
    allowPlacement = this._checkPlacement(item, rowIdx, cellIdx);
} while (!allowPlacement && cellIdx !== -1);

After the loop I just had to check if allowPlacement && cellIdx !== -1 and place the item at that index. If false I created a new row on the grid and repeated the process.

On js-signals I use a do-while to implement a very simple sorted insert.

The do-while is nothing more than a sugared version of a while(true) which means I could totally live without it as well.

// same as previous example
while(true){
    cellIdx = this._getFitIndex(item, rowIdx, cellIdx);
    allowPlacement = this._checkPlacement(item, rowIdx, cellIdx);
    if ((allowPlacement && cellIdx !== -1) || cellIdx === -1) break;
}

top-level iterators

I’ve been using mout on almost all my projects for the past year and it is really great to access the array and object iterators as top-level functions:

var forEach = require('mout/array/forEach');
forEach(arr, function(item, i){
   console.log(i, item);
});

It would be nice if JavaScript had some kind of sugar for this kind of loops (since they are so common), maybe something that doesn’t need all the function boilerplate and that works over array and objects (anything that is iterable), I would probably suggest a syntax like this:

// sugar for Array#forEach and Object#forOwn
each(arr as [item, i]) {
  console.log(i, item);
}

On Harmony there is a proposal for the for of iterator, which is very similar:

// unsure if on Array we can also get the index
for(item of arr) {
  console.log(item);
}

If I was designing a language I would definitely include a feature like that, loops should have the least amount of boilerplate/distraction/landmines as possible.

Conclusion

I will probably avoid for loops on my own projects for the foreseeable future. while loops and ES5-like Array methods FTW (map, reduce, forEach, …)


Comments

Great post! I was also introduced to programming loops through the for loop (and have been using them ever since), but you have changed my perspective. At first glace while loops are much easier to grok.

Hi, Miller. The for loop in JavaScript always did seem a bit awkward to me with three statements in one line. Although, the alternate form (the for/in loop) is still useful.

Your reverse order example isn't exactly true. You compare:

var n = 100; while (n--) { console.log(n); }

to

for(var n = 99; n >= 0; n--) { console.log(n); }

When you can do this just fine, without the need for special adjustments:

for(var n = 100; n--; ) { console.log(n); }

You later at least mention that exclusions are possible (and call it 'abuse'...), and ask why not use a while loop; when it's already answered - as a matter of preference. There are times I personally prefer to have less lines and other times I prefer more. It's nice to have the option of initializing values in one line.

While it is a matter of preference in many cases, the comparison isn't very valid in at least that case as you use an over-complicated example and make statements of what is 'needed' when it's actually not. The for() version is not really as different from the while() version as you imply.

For loops are still faster, unless you need to count backwards, then While(--length) is the best choice: http://jsperf.com/fastest-while-loops/4 Though a pre-processor that swaps While loops for For versions, or CoffeeScript can help programmers keep their favorite nomenclatures while maxing out speed.

Leave a Comment

Please post only comments that will add value to the content of this page. Please read the about page to understand the objective of this blog and the way I think about it. Thanks.

Comments are parsed as Markdown and you can use basic HTML tags (a, blockquote, cite, code, del, em, strong) but some code may be striped if not escaped (specially PHP and HTML tags that aren't on the list). Line and paragraph breaks automatic. Code blocks should be indented with 4 spaces.