如何使用索引迭代生成器?

问题描述 投票:0回答:4

使用 javascript 中的数组,获取迭代的当前索引很容易。您可以使用

forEach
并且索引是第二个条目,或者使用
for...of
.entries()
以及数组解包。

但是生成器没有

.entries()
方法。如何获取
for...of
循环中生成器的当前索引?

我基本上想要:

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
javascript ecmascript-6 generator
4个回答
5
投票

2024 年,我将更新 2018 年的答案,并提到第 3 阶段获得支持的Iterator helpers 功能(Node 22.9+、Chrome/Edge 122+、Opera 108+)。迭代器原型函数集包括一个

forEach
方法,它提供回调函数的索引:

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

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


2018年的原始答案:

不建议将内容添加到内置原型中,但如果您确实希望代码像那样工作(在任何生成器上调用

.entries()
),那么您可以按如下方式进行:

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);
}

然而,定义一个效用函数更为谨慎。

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);
}


2
投票

没有内置的方法可以做到这一点 - 生成器必须产生包含索引的东西。例如:

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);
}

如果您无法修改想要获取其索引的生成器,则可以将其放入另一个生成器中,该生成器跟踪索引(或者您可以在外部的每次迭代中递增):

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);
}


1
投票

一种稍微不同的方法可能是使

myGen()
成为一个常规函数,返回遵守迭代器协议的对象而不是生成器。然后你可以给它一个
entries()
方法。它的工作方式与生成器略有不同(您不能直接对其调用
next()
)。但它是独立的,并且应该在需要迭代器的情况下按预期工作:

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}`)
}


1
投票

但是生成器没有

.entries()
方法。我如何获取当前 我的
for...of
循环中生成器的索引?

您可以在数组文字和

.entries()
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);
}

© www.soinside.com 2019 - 2024. All rights reserved.