ES7 Array and Generator comprehensions
This commit is contained in:
		@@ -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.
 | 
			
		||||
 | 
			
		||||
[
 | 
			
		||||
  
 | 
			
		||||
](/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  | 
		Reference in New Issue
	
	Block a user