ES7 Array and Generator comprehensions
Array comprehension is a new feature proposed for ES7, with a new syntax to create new arrays from existing iterables, comprehensions can replace map and filter.
Generator comprehension brings the same feature to generators, this is a more useful feature as it removes the need to write new generators for simple map/filter operations.
Generator comprehensions allow us to easily write single-line generators, which can replace our arrays in some situations, you might ask why we might consider replacing arrays with generators, the most important reason is their laziness. I’ve explained laziness later in the article.
Comprehensions are currently only supported by Firefox, use Firefox 30+ or Babel to run the examples. The Node.js version of examples using generator
function* ()s is available at the repository (doesn’t require transpilation, use latest node).
The syntax is pretty simple, you can only use
for of and
if inside comprehensions.
You can also nest comprehensions:
This is one of the most important advantages of generators over arrays and things alike. The reason why I’m including this here is to give you a good reason to write generators instead of arrays while generator comprehensions make it extremely easy to write them — this is a proof of their usefulness.
In programming, laziness means doing nothing until the results are requested or in simpler terms, avoiding unnecessary work. For example, when you create an array and map it, the result will be evaluated no matter you need it now or not, you need the whole thing or a part of it, etc.
Take this example:
You know what happens here, first, map is evaluated, returning thousands of squared numbers, then the first element is returned. We must allocate and evaluate the whole squared array to be able to know about it’s first or second element.
Think of optimizing it, is it possible to get the desired result without storing temporary arrays in memory? Can we get the first number directly without consuming a big chunk of memory?
Yes, using generators, Look at this:
Let’s see what happens in this case.
Here, we create a generator which will yield numbers 0…100000, nothing is actually allocated or evaluated, we just have a generator which will return a new number every time we call
Then we use generator comprehension to create another generator which squares the numbers our
bigGenerator() yields, again, we don’t evaluate or allocate anything, we just create a generator which will call another generator’s
next() method, square the results, and yield it.
Now when we call
squared generator calls
bigArray().next(), squares the results and yields it, it doesn’t do any unnecessary work, it’s lazy.
If you profile heap/memory usage and running time, you will see the difference.
I have prepared a Node.js version of the test case. With the help of
console.time we can easily see the difference.
It’s a lot faster, with less space required, isn’t that awesome?