Array comprehensions

Non-standard. Do not use!
The array comprehensions syntax is non-standard and removed starting with Firefox 58. For future-facing usages, consider using Array.prototype.map, Array.prototype.filter, arrow functions, and spread syntax.

Obsolete since Gecko 58 (Firefox 58 / Thunderbird 58 / SeaMonkey 2.55)
This feature is obsolete. Although it may still work in some browsers, its use is discouraged since it could be removed at any time. Try to avoid using it.

The array comprehension syntax was a JavaScript expression which allowed you to quickly assemble a new array based on an existing one. However, it has been removed from the standard and the Firefox implementation. Do not use it!

Syntax

[for (x of iterable) x]
[for (x of iterable) if (condition) x]
[for (x of iterable) for (y of iterable) x + y]

Description

Inside array comprehensions, these two kinds of components are allowed:

The for-of iteration is always the first component. Multiple for-of iterations or if statements are allowed.

Array comprehension was previously proposed to be standardized in ECMAScript 2016, it provide a useful shortcut for constructing a new array based on the contents of another. Comprehensions can often be used in place of calls to map() and filter(), or as a way of combining the two.

The following comprehension takes an array of numbers and creates a new array of the double of each of those numbers.

var numbers = [1, 2, 3, 4];
var doubled = [for (i of numbers) i * 2];
console.log(doubled); // logs 2,4,6,8

This is equivalent to the following map() operation:

var doubled = numbers.map(i => i * 2);

Comprehensions can also be used to select items that match a particular expression. Here is a comprehension which selects only even numbers:

var numbers = [1, 2, 3, 21, 22, 30];
var evens = [for (i of numbers) if (i % 2 === 0) i];
console.log(evens); // logs 2,22,30

filter() can be used for the same purpose:

var evens = numbers.filter(i => i % 2 === 0);

map() and filter() style operations can be combined into a single array comprehension. Here is one that filters just the even numbers, then creates an array containing their doubles:

var numbers = [1, 2, 3, 21, 22, 30];
var doubledEvens = [for (i of numbers) if (i % 2 === 0) i * 2];
console.log(doubledEvens); // logs 4,44,60

The square brackets of an array comprehension introduce an implicit block for scoping purposes. New variables (such as i in the example) are treated as if they had been declared using let. This means that they will not be available outside of the comprehension.

The input to an array comprehension does not itself need to be an array; iterators and generators can also be used.

Even strings may be used as input; to achieve the filter and map actions (under Array-like objects) above:

var str = 'abcdef';
var consonantsOnlyStr = [for (c of str) if (!(/[aeiouAEIOU]/).test(c)) c].join(''); // 'bcdf'
var interpolatedZeros = [for (c of str) c + '0' ].join(''); // 'a0b0c0d0e0f0'

Again, the input form is not preserved, so we have to use join() to revert back to a string.

Examples

Simple array comprehensions

[for (i of [1, 2, 3]) i * i ];
// [1, 4, 9]

var abc = ['A', 'B', 'C'];
[for (letters of abc) letters.toLowerCase()];
// ["a", "b", "c"]

Array comprehensions with if statement

var years = [1954, 1974, 1990, 2006, 2010, 2014];
[for (year of years) if (year > 2000) year];
// [2006, 2010, 2014]
[for (year of years) if (year > 2000) if (year < 2010) year];
// [2006], the same as below:
[for (year of years) if (year > 2000 && year < 2010) year];
// [2006]

Array comprehensions compared to map and filter

An easy way to understand array comprehension syntax, is to compare it with the Array map and filter methods:

var numbers = [1, 2, 3];

numbers.map(function (i) { return i * i });
numbers.map(i => i * i);
[for (i of numbers) i * i];
// all are [1, 4, 9]

numbers.filter(function (i) { return i < 3 });
numbers.filter(i => i < 3);
[for (i of numbers) if (i < 3) i];
// all are [1, 2]

Array comprehensions with two arrays

Using two for-of iterations to work with two arrays:

var numbers = [1, 2, 3];
var letters = ['a', 'b', 'c'];

var cross = [for (i of numbers) for (j of letters) i + j];
// ["1a", "1b", "1c", "2a", "2b", "2c", "3a", "3b", "3c"]

var grid = [for (i of numbers) [for (j of letters) i + j]];
// [
//  ["1a", "1b", "1c"],
//  ["2a", "2b", "2c"],
//  ["3a", "3b", "3c"]
// ]

[for (i of numbers) if (i > 1) for (j of letters) if(j > 'a') i + j]
// ["2b", "2c", "3b", "3c"], the same as below:

[for (i of numbers) for (j of letters) if (i > 1) if(j > 'a') i + j]
// ["2b", "2c", "3b", "3c"]

[for (i of numbers) if (i > 1) [for (j of letters) if(j > 'a') i + j]]
// [["2b", "2c"], ["3b", "3c"]], not the same as below:

[for (i of numbers) [for (j of letters) if (i > 1) if(j > 'a') i + j]]
// [[], ["2b", "2c"], ["3b", "3c"]]

Specifications

Was initially in the ECMAScript 2015 draft, but got removed in revision 27 (August 2014). Please see older revisions of ES2015 for specification semantics.

Browser compatibility

Supported nowhere. Historically supported in Firefox 30 till 58.

Differences to the older JS1.7/JS1.8 comprehensions

JS1.7/JS1.8 comprehensions are removed from Gecko starting with version 46 (bug 1220564).

Old comprehensions syntax (do not use anymore!):

[X for (Y in Z)]
[X for each (Y in Z)]
[X for (Y of Z)]

Differences:

  • ESNext comprehensions create one scope per "for" node instead of the comprehension as a whole.
    • Old: [()=>x for (x of [0, 1, 2])][1]() // 2
    • New: [for (x of [0, 1, 2]) ()=>x][1]() // 1, each iteration creates a fresh binding for x.
  • ESNext comprehensions start with "for" instead of the assignment expression.
    • Old: [i * 2 for (i of numbers)]
    • New: [for (i of numbers) i * 2]
  • ESNext comprehensions can have multiple if and for components.
  • ESNext comprehensions only work with for...of and not with for...in iterations.

See Bug 1220564, comment 42 for suggestions on updating code.

See also