Exploring CoffeeScript Part 5 – Ranges, Loops and Comprehensions

August 2nd, 2011

For this blog post, we’re going to discuss ranges, loops and comprehensions in CoffeeScript.

Also check out the previous installments:

Ranges

    Again a feature that is heavily inspired by Ruby. Using a range we can define an array of consecutive numbers by only specifying the first and the last number.

    numbers = [1..10]
    console.log numbers        # Outputs [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

This is a so-called inclusive range which is denoted by the two dots separating the bounding numbers. CoffeeScript also provides the concept of an exclusive range which is denoted by three dots instead of two.

numbers = [1...10]
console.log numbers        # Outputs [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

In this case the number of the upper boundary is excluded. Ranges are a small addition to the syntax that can come in handy from time to time.

Loops

As with JavaScript, CoffeeScript also provides us with two different kinds of for loops: one for iterating over an array and one for iterating over the properties of an object.

download = (podcast) ->
    console.log 'Downloading ' + podcast

favoritePodcasts = ['Astronomy Cast', 'Hardcore History', 
                    'Talking Shop Down Under', 'Pluralcast']

for podcast in favoritePodcasts
    download podcast

The for .. in syntax in CoffeeScript is used for iterating over an array while the next example show how a for .. of loop is used for iterating over the properties of an object.

podcasts = 
    'Astronomy Cast': 'http://www.astronomycast.com/',
    'Hardcore History': 'http://www.dancarlin.com/disp.php/hh',
    'Talking Shop Down Under': 'http://www.talkingshopdownunder.com/',
    'Pluralcast': 'http://www.pluralsight-training.net/microsoft/pluralcast/default.aspx'

for key, value of podcasts
    console.log key + ' - ' + value    

A for .. in loop translates to a traditional for loop in JavaScript while the for .. of loop translates to a for .. in loop in JavaScript.  

// Iterating over an array in JavaScript
for (_i = 0, _len = favoritePodcasts.length; _i < _len; _i++) {
    ...
}

// Iterating over the properties of an object in JavaScript
for (key in podcasts) {
    value = podcasts[key];
    ...
}

In the example where we iterate over an array, the for .. in loop can be more compact using the postfix notation. 

download podcast for podcast in favoritePodcasts

How nice is that! Let’s look at some more sweet syntax that CoffeeScript brings us. We can use the ‘when’ keyword for filtering.

for number in [1..10] when number > 5
    console.log number        # Outputs '6, 7, 8, 9, 10'

Filters are supported by both for .. in and for .. of loops. We can also obtain the current index when looping over an array.

favoritePodcasts = ['Astronomy Cast', 'Hardcore History', 
                    'Talking Shop Down Under', 'Pluralcast']
for podcast, i in favoritePodcasts
    console.log (i+1) + '/ ' + podcast

This is only possible when using for .. in loops. We can also use the ‘by’ keyword to loop over an array with fixed-size steps.

for number in [0..100] by 10
    console.log number        # Outputs '10, 20, 30, ..., 100'

Again, this is only possible when using for .. in loops. Besides all these nice syntax additions for for … in loops, there is also a quite helpful keyword that’s only available when using for .. of loops. We can use the ‘own’ keyword for iterating over properties that are only defined on the object itself and not on any prototype(s).

Podcast = ->
    @name = 'Astronomy Cast'
    @url = 'http://www.astronomycast.com/'

Podcast::download = ->
    console.log 'Downloading ' + @name + ' from ' + @url

podcast = new Podcast()
for own key, value of podcast
    console.log key + ' : ' + value

Note that we use the :: operator as a shorthand for denoting the prototype property. In this example we used the ‘own’ keyword to skip over any properties defined on the prototype objects, which is simply some syntactic sugar for a hasOwnProperty check in JavaScript. 

var Podcast, key, podcast, value;
var __hasProp = Object.prototype.hasOwnProperty;

Podcast = function() {
    this.name = 'Astronomy Cast';
    return this.url = 'http://www.astronomycast.com/';
};

Podcast.prototype.download = function() {
    return console.log('Downloading ' + this.name + ' from ' + this.url);
};

podcast = new Podcast();
for (key in podcast) {
    if (!__hasProp.call(podcast, key)) continue;
    
    value = podcast[key];
    console.log(key + ' : ' + value);
}

Let’s take this a bit further by diving into comprehensions.

Comprehenions

Everything is an expression in CoffeeScript, which means that this also applies to loops. What better way to demonstrate this than a simple code example? Suppose we want to create an array with even numbers by looping over another array of numbers. This is how it can be solved using the CoffeeScript way.

evenNumbers = (number for number in [1..10] when number % 2 is 0)
console.log evenNumbers        # Outputs [ 2, 4, 6, 8, 10 ]

A for loop in CoffeeScript always returns an array with the result of every iteration which in this case are the even numbers from the first array. Note that we need to enclose this comprehension with parentheses because otherwise we only get the last even number instead of an array with all the even numbers.

If you’re not yet impressed by this language feature, then checkout the following one-line FizzBuzz implementation.

fizzbuzz = (['fizz' unless i%3] + ['buzz' unless i%5] or i for i in [1..100])
console.log fizzbuzz

The implementation comes from this blog post of Ricardo Tomasi which is highly recommended.

While

To make things complete, CoffeeScript also provides a while loop.

count = 0
tenfolds = while (count += 1) <= 10
    count * 10
    
console.log tenfolds    

The while loop in CoffeeScript behaves the same as the one in JavaScript except that it also returns an array of values.

That’s it for now. Until next time.

  • Marcus Swope

    Is the postfix notation example that you gave:

    download podcast for podcast in favoritePodcasts

    another example of a comprehension?

    • Anonymous

      Yes, it was a sneak peek :-)