ariya.io About Talks Articles

ECMAScript 6 and Array Comprehension

4 min read

Many modern programming languages support list comprehension, a concise way to create a list based another list where each entry is the result of some operations. If comprehension is used properly, it eliminates the need for the traditional and error-prone manual iteration. Next-generation JavaScript will have the similar feature via array comprehension.

First of all, let’s do a quick refresh on Array’s map and filter functions.

Array.prototype.map

Section 15.4.4.19 of the official ECMAScript 5.1 specification defines the official behavior of Array.prototype.map. This function returns a new array resulting from applying the given callback function to each entry.

Two quick examples:

[1, 2, 3].map(function (i) { return i * i }); // [1, 4, 9]
[650,123,4567].map(String).join('-'); // "650-123-4567"

This facilitates a one-liner to build a sequence of numbers:

Array.apply(, Array(3)).map(function(x, y) { return y }); // [0, 1, 2]

or even English alphabets ‘ABCDEFGHIJKLMNOPQRSTUVWXYZ’:

Array.apply(, Array(26)).map(function(x,y) {
  return String.fromCharCode(y + 65);
}).join('');

For other variants, see also Brandon Benvie’s usage of apply-map and Ben Alman’s Object.keys technique.

Update: For details, see also my other blog post Sequences with JavaScript Array.

Array.prototype.filter

Section 15.4.4.20 of the official ECMAScript 5.1 defines the official behavior of Array.prototype.filter. As the name says, this function lets you include or exclude some entries of the array based on some certain criteria. Take a look at the following example:

[1,4,2,3,-8].filter(function(i) { return i < 3 }); // [1, 2, -8]

Let’s extend the previous sequence number generation, say to have only odd number:

Array.apply(, Array(6)).map(function(x,y) { return y }).
filter(function(x,y) { return y & 1 }); // [1, 3, 5]

We can also do a complicated dance to print all consonants by excluding the vowels:

Array.apply(, Array(26)).map(function(x,y) { return String.fromCharCode(y + 65) }).
filter(function(s) { return 'AEUIO'.indexOf(s) <  }).join('');

Real-world applications are likely more practical than the above snippets. It could be something like:

Give me the list of house prices in a certain ZIP code

What is the total expense of our Engineering department?

Find the best paid professions of Gen X

Array Comprehension

Array comprehension is a syntax feature which has been available in Firefox for a while. It is however not part of the 5th edition of ECMAScript and hence no other browser supports it. The good news is that array comprehension is being incorporated into the next ECMAScript 6. The latest 2012/12/21 draft includes the grammar of array comprehension in section 11.1.4.2. Update: The following code examples have been adjusted to follow the latest comprehension syntax (left-to-right).

An easy way to understand how array comprehension works is by comparing it with map and filter. See the following two lines, they give the same exact result. The second line is something you have seen in the previous map example.

[for (i of [1, 2, 3]) i * i]; // [1, 4, 9]
[1, 2, 3].map(function (i) { return i * i }); // [1, 4, 9]

The fun part is when you use two for clauses or more. The following line creates a list which contains the references to all 64 possible squares in a chess board, from ‘a1’ to ‘h8’.

[for (x of 'abcdefgh'.split('')) for (y of '12345678'.split('')) (x+y)];

If this still looks confusing, I highly recommend understanding the syntax tree, for example by using Esprima’s syntax visualization.

es6comprehension

Note: Firefox’s array comprehension also support for-in form (I am not sure whether this will make it into ES6). It can simplify some construct, generating a sequence of numbers can be rewritten as [j for (j in Array.apply(0, Array(3)))].

Filtering using array comprehension is straightforward. Again, compare the two different forms here:

[for (i of [1,4,2,3,-8]) if (i < 3) i];
[1,4,2,3,-8].filter(function(i) { return i < 3 }); // [1, 2, -8]

And the simplification of printing the sequence of all alphabets:

[for (i of  Array.apply(, Array(26)).map(function(x, y) { return y; }))
String.fromCharCode(65 + i)].join('');

and just the consonants:

[for (j of
  [for (i of Array.apply(, Array(26)).map(function(x, y) { return y; }))
   String.fromCharCode(65 + i)]
) if ('AEUIO'.indexOf(j) < ) j];

As an exercise, analyze the following expression. What it does is producing the list of all prime numbers less than 100. You can see that there is no need for a manual loop at all. Note that since we are talking about next-generation JavaScript, we also use the arrow function (Section 13.2) to shorten the incantation.

[
  for (x of Array.apply(, Array(99)).map((x, y) => y + 2))
    if ([
      for (i of Array.apply(, Array(1 + Math.round(Math.sqrt(x)))).map((x,y) => y))
      if ((i > 1) && ((x % i) === ))
      (x % i)
    ].length === )
  x
];

With the support for array comprehension, JavaScript is getting more and more functional. If you come from Python, Haskell, Scala, or another modern language, you won’t feel so powerless anymore!

Related posts:

♡ this article? Explore more articles and follow me Twitter.

Share this on Twitter Facebook