Blog

Where we discuss our work, thoughts, and process

Paradigms of Iteration in JavaScript

One of the joys of programming is that no matter how simple a problem may seem there are always tons of ways to solve it. It can be good practice to go back and revisit fundamentals by solving simple problems with as many implementations as you can think of. In this post we'll explore approaches to basic iteration in JavaScript.

This style of exercise is a good interviewing technique, too, because it's open ended and leads to good discussions. The focus isn't a tricky, wacky problem you're seeing for the first time ever, but bread and butter programming awareness, command, and comfort. It's a good warmup to get a sense of strengths and depth of knowledge.

A Basic JavaScript Iteration Problem

Iterate through an array of letters, in order, one-at-a-time, and print them using console.print. Here are your letters.

var letters = [ 'a', 'b', 'c' ];

Your solutions should achieve the equivalent of the following hard-coded, brute force solution, except they should handle arrays of any length.

console.log(letters[0]);
console.log(letters[1]);
console.log(letters[2]);

How many different solutions can you come up with? Loop constructs, functions, library functions, you can use any means possible.

Go on, pop open your developer console. Play around.

Did you cover the loop constructs (for, while, do..while, for..in)?

Did you write an enumeration object?

What library functions are in your tool belt (Array.forEach, jQuery.each, underscore.each,...)?

Did you roll your own naive each function?

Did you implement your each function recursively, without using a loop construct at all?

Did you do something mind blowing not even mentioned? Awesome, leave a comment!

Iterative Solutions

When learning a language like Java, C, or JavaScript, one of the first things we're often taught are language-defined looping constructs. With JavaScript's functional capabilities it's possible to avoid using looping constructs almost altogether. We're talking fundamentals, here, though, and it's important to be comfortable with each basic construct. Let's take a look at a while loop based solution.

var i = 0;
while( i < letters.length) {
    console.log(letters[i]);
    i++;
}

How would you transform that while loop to a for loop? Why is a do..while loop awkward to use here? What risk does for..in pose looping through an Array object?

Functional Solutions

JavaScript's treatment of functions as first-class values enables us to avoid writing loop constructs in a wide variety of situations. Resig's jQuery can largely be thanked for encouraging this style of iteration throughout the web development community. Let's solve the problem with jQuery's each function.

$.each( letters, function(i) { console.log(letters[i]); } );
// or
$.each( letters, function(i, letter) { console.log(letter); } );
// or - idiomatic jQuery
$.each( letters, function() { console.log(this); });  

jQuery's each expects a callback with a signature of (index,value). This is a different signature than ECMAScript's Array.forEach function which has the inverse and provides a reference to the array as the third argument (value,index,array).

letters.forEach( function(letter) { console.log(letter) } );
// or
['a','b','c'].forEach( function(letter, i, letters) { console.log(letters[i]); });

These iteration functions are nice. Have you implemented one before? If you wrote a for loop you can do it.

Let's implement a naive equivalent that passes just each element's value to the user-defined function.

var each = function( array, fn ) {
    for( var i = 0; i < array.length; i++) {
        fn(array[i]);
    }
};
each( letters, function(letter) { console.log(letter); } );

We've come up with a "functional" solution by abstracting away an iterative solution. A truly functional solution shouldn't need a loop at all. Functions only!

Recursive Solutions

If we can't use a loop construct we'll need to "jump" back to the top of our iteration by invoking a function recursively. Like a loop we'll need a base case an a recursive case. When will we exit and move back down the stack? Here's one take:

var each = function( array, fn, i ) {
    if(i === undefined) { i = 0; }
    if(i < array.length) {
        fn(array[i]);
        each(array, fn, i+1);
    }
}
each( letters, function(letter) { console.log(letter); } );

Look ma, no loops!

Object-Oriented Style Enumeration Solutions

Enumeration/iteration objects are a commonly used pattern in object-oriented programming. This style isn't frequently used in JavaScript, but it's interesting enough to deserve a mention.

Iteration state, in this problem the index in the array, as well as the logic for traversal (incrementing the index from 0 to the end of the array), are both encapsulated within the iterator object.

var iterator = {
    i:       0,
    hasNext: function() {
        return iterator.i < letters.length;
    }
    next:   function() {
        return letters[iterator.i++];
    }
};
while( iterator.hasNext() ) {
    console.log(iterator.next());
}

What is interesting about this approach is you can use different traversal orders without actually changing the logic of any loops that depend on the iterator object. Can you write a reverseIterator? Can you write a functional each to take an iterator object rather than a plain old array? What is interesting about the functional style, about the object-oriented style, about combining the two?

Wrap up

It's good for everyone to get back to the fundamentals every now and then and practice technique. Even trivial programming problems, like printing an array of letters, have non-trivial solutions. Exploring solution spaces can cover a lot of ground and act as a great tool for identifying strengths and discomforts.

What other solutions did you come up with?

Comments

Aeron's avatar
Aeron
I think the jquery example should look like:

$.each( letters, function() { console.log(this); } );

Both are correct, but the above is more inline with the jquery mo of writing less.
T1B0's avatar
T1B0
This one is missing:

Array.prototype.forEach.call(elems, function(el){ console.log(el); })

Shorthand :

[].forEach.call(elems, function(el){console.log(el); })
Kris Jordan's avatar
Kris Jordan NMC team member
@Aeron I've added the idiomatic use of 'this' in jQuery's `each` example, too. Great addition. On its own the magical binding of `this` may be confusing, in the context of other examples it is easy to understand.

@T1B0 Interesting permutation on calling `forEach` directly from the `prototype` in a more traditional, functional style. The traditional `Array.forEach` method invocation is demoed above. Invoking it with `call` or `apply` might be interesting follow-up questions to probe understanding of function invocation capabilities and `this` binding.
Rick Waldron's avatar
Rick Waldron
`Array.forEach` incorrectly implies a static method of the global Array object, when you obviously mean `Array.prototype.forEach`. There is a very distinct difference. Additionally, there might be confusion where SpiderMonkey actually _does_ implement a static `Array.forEach` (in addition to the `Array.prototype.forEach`).
Nick Breen's avatar
Nick Breen
Your OO Enumerator code is missing a comma between hasNext and next functions.

Thanks for writing this blog post. Excellent exercise.

Leave a comment

Real Time Web Analytics