How do I get the nth value of a JavaScript generator?
How can I get the nth value of the generator?
function *index() {
let x = 0;
while(true)
yield x++;
}
// the 1st value
let a = index();
console.log(a.next().value); // 0
// the 3rd value
let b = index();
b.next();
b.next();
console.log(b.next().value); // 2
// the nth value?
let c = index();
let n = 10;
console.log(...); // 9
source to share
You can define an enumeration method, for example in python :
function *enumerate(it, start) {
start = start || 0;
for(let x of it)
yield [start++, x];
}
and then:
for(let [n, x] of enumerate(index()))
if(n == 6) {
console.log(x);
break;
}
http://www.es6fiddle.net/ia0rkxut/
In the same lines, you can also implement pythonic range
and islice
:
function *range(start, stop, step) {
while(start < stop) {
yield start;
start += step;
}
}
function *islice(it, start, stop, step) {
let r = range(start || 0, stop || Number.MAX_SAFE_INTEGER, step || 1);
let i = r.next().value;
for(var [n, x] of enumerate(it)) {
if(n === i) {
yield x;
i = r.next().value;
}
}
}
and then:
console.log(islice(index(), 6, 7).next().value);
http://www.es6fiddle.net/ia0s6amd/
Real world implementation will take a little more work, but you got the idea.
source to share
As TJ Crowder pointed out , there is no way to go directly to the n
th element , since the values ββare generated on demand and only the instantaneous value can be retrieved using the function next
. Thus, we need to explicitly track the number of consumed items.
The only solution is to use a loop, and I prefer to repeat it with for..of
.
We can create a function like this
function elementAt(generator, n) {
"use strict";
let i = 0;
if (n < 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
if (i++ == n) {
return value;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
and then call it like this:
console.log(elementAt(index(), 10));
// 10
Another useful function might be take
that will allow you to take the first n
elements from the generator, for example
function take(generator, n) {
"use strict";
let i = 1,
result = [];
if (n <= 0) {
throw new Error("Invalid index");
}
for (let value of generator) {
result.push(value);
if (i++ == n) {
return result;
}
}
throw new Error("Generator has fewer than " + n + " elements");
}
console.log(take(index(), 10))
// [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
source to share
You can create an array of size n and use Array.from
its second argument to get the values ββyou want. So, assuming which iter
is an iterator from a generator gen
:
var iter = gen();
Then the first n values ββcan be chosen as follows:
var values = Array.from(Array(n), iter.next, iter).map(o => o.value)
... and when you are only interested in the value of n th you can skip the part map
and do:
var value = Array.from(Array(n), iter.next, iter).pop().value
Or:
var value = [...Array(n)].reduce(iter.next.bind(iter), 1).value
The downside is that you are still (temporarily) allocating an array of size n.
source to share