Generator comprehensions

Non-standard. Do not use!
The generator comprehensions is non-standard, and it's unlikely to be added to ECMAScript,  For future-facing usages, consider using generator.

The generator comprehension syntax is a JavaScript expression which allows you to quickly assemble a new generator function based on an existing iterable object. Comprehensions exist in many programming languages.

See below for differences to the old generator expression syntax in SpiderMonkey, based on proposals for ECMAScript 4.

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 generator 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.

A significant drawback of array comprehensions is that they cause an entire new array to be constructed in memory. When the input to the comprehension is itself a small array the overhead involved is insignificant — but when the input is a large array or an expensive (or indeed infinite) generator the creation of a new array can be problematic.

Generators enable lazy computation of sequences, with items calculated on-demand as they are needed. Generator comprehensions are syntactically almost identical to array comprehensions — they use parentheses instead of braces— but instead of building an array they create a generator that can execute lazily. You can think of them as short hand syntax for creating generators.

Suppose we have an iterator it which iterates over a large sequence of integers. We want to create a new iterator that will iterate over their doubles. An array comprehension would create a full array in memory containing the doubled values:

var doubles = [for (i in it) i * 2];

A generator comprehension on the other hand would create a new iterator which would create doubled values on demand as they were needed:

var it2 = (for (i in it) i * 2);
console.log(it2.next()); // The first value from it, doubled
console.log(it2.next()); // The second value from it, doubled

When a generator comprehension is used as the argument to a function, the parentheses used for the function call means that the outer parentheses can be omitted:

var result = doSomething(for (i in it) i * 2);

The significant difference between the two examples being that by using the generator comprehension, you would only have to loop over the 'obj' structure once, total, as opposed to once when comprehending the array, and again when iterating through it.

Examples

Simple generator comprehensions

(for (i of [1, 2, 3]) i * i );
// generator function which yields 1, 4, and 9
[...(for (i of [1, 2, 3]) i * i )];
// [1, 4, 9]
var abc = ['A', 'B', 'C'];
(for (letters of abc) letters.toLowerCase());
// generator function which yields "a", "b", and "c"

Generator comprehensions with if statement

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

Generator comprehensions compared to generator function

An easy way to understand generator comprehension syntax, is to compare it with the generator function.

Example 1: Simple generator.

var numbers = [1, 2, 3];
// Generator function
(function*() {
  for (let i of numbers) {
    yield i * i;
  }
})();
// Generator comprehension
(for (i of numbers) i * i );
// Result: both return a generator which yields [1, 4, 9]

Example 2: Using if in generator.

var numbers = [1, 2, 3];
// Generator function
(function*() {
  for (let i of numbers) {
    if (i < 3) {
      yield i * 1;
    }
  }
})();
// Generator comprehension
(for (i of numbers) if (i < 3) i);
// Result: both return a generator which yields [1, 2]

Specifications

Generator comprehensions were 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

Feature Chrome Firefox (Gecko) Internet Explorer Opera Safari
Basic support No support 30 (30) No support No support No support
Feature Android Chrome for Android Firefox Mobile (Gecko) IE Mobile Opera Mobile Safari Mobile
Basic support No support No support 30.0 (30) No support No support No support

SpiderMonkey-specific implementation notes

  • let as an identifier is not supported as let is currently only available to JS version 1.7 and XUL scripts tags.
  • Destructuring in comprehensions is not supported yet (bug 980828).

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

JS1.7/JS1.8 comprehensions are removed from Gecko 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:

  • ES7 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.
  • ES7 comprehensions start with "for" instead of the assignment expression.
    • Old: (i * 2 for (i of numbers))
    • New: (for (i of numbers) i * 2)
  • ES7 comprehensions can have multiple if and for components.
  • ES7 comprehensions only work with for...of and not with for...in iterations.

See also

Document Tags and Contributors

 Contributors to this page: nmve, kdex, fscholz, arai, so_matt_basta, jwhitlock, arseniew
 Last updated by: nmve,