How to get the nth value of a JavaScript generator?

前端 未结 4 1446
萌比男神i
萌比男神i 2020-12-19 18:07

How can I get the nth value of a generator?

function *index() {
  let x = 0;
  while(true)
    yield x++;
}

// the 1st value
let a = index();
console.log(a.         


        
相关标签:
4条回答
  • 2020-12-19 18:21

    A simple loop will do:

    let n = 10,
        iter = index();
    while (--n > 0) iter.next();
    console.log(iter.next().value); // 9
    
    0 讨论(0)
  • 2020-12-19 18:24

    You can define an enumeration method like 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/

    Along the same lines, one can also reimplement 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/

    A real-world implementation would require a bit more work, but you got the idea.

    0 讨论(0)
  • 2020-12-19 18:36

    You could create an array of size n, and use Array.from and its second argument to fetch the values you need. So assuming iter is the iterator from generator gen:

    var iter = gen();
    

    Then the first n values can be fetched as follows:

    var values = Array.from(Array(n), iter.next, iter).map(o => o.value)
    

    ...and when you are only interested in the nth value, you could skip the map part, 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 still (temporarily) allocate an array of size n.

    0 讨论(0)
  • 2020-12-19 18:40

    As T.J. Crowder pointed out, there is no way to get to the nth element directly, as the values are generated on demand and only the immediate value can be retrieved with the next function. So, we need to explicitly keep track of the number of items consumed.

    The only solution is using a loop and I prefer iterating 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 invoke it like this

    console.log(elementAt(index(), 10));
    // 10
    

    Another useful function might be, take, which would allow you to take first n elements from a generator, like this

    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 ]
    
    0 讨论(0)
提交回复
热议问题