If there's anything better than CoffeeScript, it's the amazing quality of documentation available. The main homepage, coffeescript.org is a stellar example many projects should be copying. And then there's the The Little Book on CoffeeScript which I have nothing but praise for (as a Little Book author myself!).
Nevertheless, I find that most resources don't properly explain CoffeeScript comprehensions. So, what's a comprehension? .NET readers would find it similar to LINQ...it's essentially a way to loop over and act on values in arrays or hashes.
In CoffeeScript, if you want to loop through an array, you use
for in. To loop through hashes/objects you use
for of. For example:
heroes = ['leto', 'duncan', 'goku'] for hero in heroes console.log(hero) # Or, including the index for hero, index in heroes console.log('The hero at index %d is %s', index, hero) likes = leto: 'spice' paul: 'chani' duncan: 'murbella' for key of likes console.log(key) # Or, including the value for key, value of likes console.log('%s likes %s', key, value)
Now, that's pretty basic and understandable. I understood the syntax around comprehensions when I recognized the similarities to statement modifiers. Statement modifiers are a neat and useful way in CoffeeScript and in Ruby to execute an
# instead of if x == 0 x = 1 # we ca do x = 1 if x == 0
If you aren't used to these, it might seem like pretty useless syntactical sugar, but I believe that they contribute enough to code readability to make them worth using. Anyways, even though most people are used to thinking of code executing from left to right, I think the above statement modifiers (which first check the condition on the right) are easy enough to understand.
Being familiar with this type of syntactical trick, we can go back to our loops fancy them up:
heroes = ['leto', 'duncan', 'goku'] console.log(hero) for hero in heroes likes = leto: 'spice' paul: 'chani' duncan: 'murbella' console.log('%s likes %s', key, value) for key, value of likes
Comprehensions allows for conditions via
when, so we do:
heroes = ['leto', 'duncan', 'goku'] # boring for hero, index in heroes if index % 2 == 0 console.log(hero) # cool console.log hero for hero, index in heroes when index % 2 == 0
Notice also that I dropped the parenthesis around the
console.log. In CoffeeScript parenthesis are often optional and people tend to not use them while writing comprehensions.
There are other things you can do with compressions. For example, you won't always want to execute some code (like
console.log) on each item. Instead you might want to select or map the results into another variable:
evenHeroes = (hero for hero, index in heroes when index % 2 == 0)
The parenthesis in the above code are critical. Without them, only the last value of our comprehension ('goku') will be assigned. With them, the filtered array is assigned. The very first
hero in the above code is what's being selected from our compression.