javascript - How to iterate over a generator with indexes? - Stack Overflow

With arrays in javascript, getting the current index for iteration is easy. You can either use forEach

With arrays in javascript, getting the current index for iteration is easy. You can either use forEach and the index is the second entry, or use for...of and .entries() and array unpacking.

But generators have no .entries() method. How do I get the current index for a generator in my for...of loop?

I basically want:

function* myGen(){
    let i = 0;
    while(true) {
        i+=1;
        yield i;
    }
}

for(let [j, index] of myGen().entries()) { //<-- I want .entries() but for a Generator
    //...
}
//Running the above produces TypeError: myGen(...).entries(...) is not a function or its return value is not iterable

With arrays in javascript, getting the current index for iteration is easy. You can either use forEach and the index is the second entry, or use for...of and .entries() and array unpacking.

But generators have no .entries() method. How do I get the current index for a generator in my for...of loop?

I basically want:

function* myGen(){
    let i = 0;
    while(true) {
        i+=1;
        yield i;
    }
}

for(let [j, index] of myGen().entries()) { //<-- I want .entries() but for a Generator
    //...
}
//Running the above produces TypeError: myGen(...).entries(...) is not a function or its return value is not iterable
Share Improve this question asked Dec 8, 2018 at 21:37 CobertosCobertos 2,26327 silver badges45 bronze badges
Add a ment  | 

4 Answers 4

Reset to default 5

In 2024 I'm updating this answer from 2018 with the mention of the Iterator helpers feature in stage 3 that is getting support (Node 22.9+, Chrome/Edge 122+, Opera 108+). The set of Iterator prototype functions includes a forEach method, which provides the index to the callback function:

// Demo
function* myGen(){
    let i = 64;
    while(i < 70) {
        i+=1;
        yield String.fromCharCode(i);
    }
}

myGen().forEach((value, index) => {
    console.log(value, index);
});


Original answer from 2018:

It is not advisable to add things to a built-in prototype, but if you really want your code to work like that (calling .entries() on any generator), then you could proceed as follows:

const Generator = Object.getPrototypeOf(function* () {});

Generator.prototype.entries = function * () {
    let i = 0;
    for (let value of this) {
        yield [i++, value];
    }
}

// Demo
function* myGen(){
    let i = 64;
    while(i < 70) {
        i+=1;
        yield String.fromCharCode(i);
    }
}

for(let [index, value] of myGen().entries()) { //<-- Now you have .entries() on a Generator
    console.log(index, value);
}

It is more prudent however to define a utility function.

const GeneratorUtils = {
    * entriesOf(iter) {
        let i = 0;
        for (let value of iter) {
            yield [i++, value];
        }
    }
};

// Demo
function* myGen(){
    let i = 64;
    while(i < 70) {
        i+=1;
        yield String.fromCharCode(i);
    }
}

for(let [index, value] of GeneratorUtils.entriesOf(myGen())) {
    console.log(index, value);
}

There's no built-in way to do it - the generator will have to yield something that contains the index. For example:

function* myGen(){
  let index = 0;
    while(index < 10) {
      const item = 'foo' + index;
      yield { item, index };
      index++;
    }
}

for(const { item, index } of myGen()) {
  console.log('item: ' + item);
  console.log('index: ' + index);
}

If you can't modify a generator that you want to also get the index of, you can put it inside another generator that does keep track of the index (or you could just increment on every iteration outside):

function* unmodifiableGen(){
  // index is private, is not being yielded
  let index = 0;
  while(index < 10) {
    yield Math.random();
    index++;
  }
}
function* generatorCounter(gen) {
  // this index *will* be yielded:
  let index = 0;
  for (const item of gen()) {
    yield { item, index };
    index++;
  }
}

for(const { item, index } of generatorCounter(unmodifiableGen)) {
  console.log('item: ' + item);
  console.log('index: ' + index);
}

A slightly different approach might be to make myGen() a regular function that returns an object adhering to the iterator protocol rather than a generator. Then you can just give it an entries() method. It will work a little differently than a generator (you can't call next() on it directly). But it be self-contained and should work as expected in situations where an iterator is expected:

function myGen(start, stop){
   return  {
        [Symbol.iterator]: function* () {
            while(start < stop){
                yield start++
            }
        },
        entries: function* entries (){
            let i = 0
            for (n of this){
                yield [i++, n]
            }
        }
    }
}

 
let g = myGen(10, 20)
// works like a regular iterator:
console.log([...g])

// but you can also call entries():
g = myGen(2, 9)
for ([i, n] of g.entries()){
  console.log(`index: ${i}, value: ${n}`)
}

But generators have no .entries() method. How do I get the current index for a generator in my for...of loop?

You can utilize spread element preceding generator function call within an array literal and .entries() method of Array.prototype

function* myGen() {
  let i = 0;
  while (i < 10) {
    i += 1;
    yield i;
  }
}

for (const [index, value] of [...myGen()].entries()) {
  console.log(index, value);
}

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745223738a4617365.html

相关推荐

  • javascript - How to iterate over a generator with indexes? - Stack Overflow

    With arrays in javascript, getting the current index for iteration is easy. You can either use forEach

    5小时前
    10

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信