ES7 Array and Generator comprehensions
This commit is contained in:
parent
201586b097
commit
710a8d43cd
@ -8,10 +8,12 @@ categories: es7, generator, array
|
||||
|
||||
Array comprehension is a new feature proposed for ES7, with a new syntax
|
||||
to create new arrays from existing [iterables](http://www.2ality.com/2015/02/es6-iteration.html),
|
||||
comprehensions can replace `map`, `filter`.
|
||||
comprehensions can replace map and filter.
|
||||
|
||||
Generator comprehension brings the same feature to generators, this is a more
|
||||
significant change as it removes the need to write new generators for simple map / filter operations.
|
||||
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](#laziness). I've explained laziness later in the article.
|
||||
|
||||
Comprehensions are currently only supported by Firefox, use Firefox 30+ or [Babel](https://babeljs.io/repl/) to run the examples. The Node.js version using generator `function* ()`s is available at the [repository](https://github.com/mdibaiee/array-vs-generator) (doesn't require transpilation, use latest node).
|
||||
|
||||
@ -25,7 +27,7 @@ let numbers = [1,2,3,4,5];
|
||||
|
||||
let even = [ for (n of numbers) if (n % 2 === 0) n ];
|
||||
// equivalent:
|
||||
// let even = numbers.map(n => { if (n % 2 === 0) return n });
|
||||
// let even = numbers.filter(n => n % 2 === 0);
|
||||
|
||||
console.log(...even); // 2 4
|
||||
{% endhighlight %}
|
||||
@ -40,7 +42,7 @@ let generator = function* () {
|
||||
}
|
||||
|
||||
let squared = ( for (n of generator()) n * n );
|
||||
// equivalent (not lazy):
|
||||
// equivalent:
|
||||
// let squared = Array.from(generator()).map(n => n * n);
|
||||
|
||||
console.log(...squared); // 0 1 4 9 16 25
|
||||
@ -63,7 +65,7 @@ let after = function* (number) {
|
||||
}
|
||||
}
|
||||
|
||||
// for each number of 0...5, yield an array of 3 numbers after it
|
||||
// for each number 0...5, yield an array of 3 numbers after it
|
||||
let nested = ( for (n of generator())
|
||||
[ for (i of after(n)) i ]
|
||||
)
|
||||
@ -77,7 +79,7 @@ console.table(Array.from(nested));
|
||||
// 6, 7, 8
|
||||
{% endhighlight %}
|
||||
|
||||
#Lazy
|
||||
#Laziness
|
||||
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.
|
||||
@ -106,27 +108,32 @@ temporary arrays in memory? Can we get the first number directly without consumi
|
||||
Yes, using generators, Look at this:
|
||||
|
||||
{% highlight javascript %}
|
||||
let bigArray = function* () {
|
||||
let bigGenerator = function* () {
|
||||
for (let i = 0; i < 100000; i++) {
|
||||
yield i;
|
||||
}
|
||||
}
|
||||
|
||||
let squared = ( for (n of bigArray()) n * n );
|
||||
let squared = ( for (n of bigGenerator()) n * n );
|
||||
|
||||
console.log(squared.next());
|
||||
{% endhighlight %}
|
||||
|
||||
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 `next()`.
|
||||
Then we use generator comprehension to create another generator which squares the numbers our `bigArray()` generator 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.
|
||||
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.next()`, the `squared` generator calls `bigArray().next()`, squares the results and yields it, it doesn't do any unnecessary work, it's lazy.
|
||||
|
||||
[
|
||||
![Generator diagram](/img/generator-diagram.png)
|
||||
](/img/generator-diagram.png)
|
||||
{% include caption.html text='Calling squared.next() 4 times' %}
|
||||
|
||||
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 [`process.memoryUsage()`](https://nodejs.org/api/process.html#process_process_memoryusage) and [`console.time`](https://developer.mozilla.org/en-US/docs/Web/API/Console/time) we can easily see the difference.
|
||||
It's about ten times faster with two times less space used, isn't that awesome?
|
||||
It's a lot faster, with less space required, isn't that awesome?
|
||||
|
||||
[Repository: mdibaiee/array-vs-generator](https://github.com/mdibaiee/array-vs-generator)
|
||||
|
||||
@ -135,6 +142,7 @@ It's about ten times faster with two times less space used, isn't that awesome?
|
||||
If you want to know more about lazy iterators, I recommend raganwald's [Lazy Iterables in JavaScript](http://raganwald.com/2015/02/17/lazy-iteratables-in-javascript.html).
|
||||
|
||||
More:
|
||||
|
||||
[MDN: Array Comprehensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Array_comprehensions)
|
||||
|
||||
[MDN: Generator Comprehensions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Generator_comprehensions)
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 92 KiB |
BIN
img/generator-diagram.png
Normal file
BIN
img/generator-diagram.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 56 KiB |
Loading…
Reference in New Issue
Block a user