【问题标题】:How to iterate over a generator with indexes?如何迭代带有索引的生成器?
【发布时间】:2019-05-10 06:19:39
【问题描述】:

使用 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


    【解决方案1】:

    不建议将东西添加到内置原型中,但如果您真的希望您的代码像那样工作(在任何生成器上调用 .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 [j, index] of myGen().entries()) { //<-- Now you have .entries() on a Generator
        console.log(j, index);
    }

    不过,定义效用函数更为谨慎。

    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 [j, index] of GeneratorUtils.entriesOf(myGen())) {
        console.log(j, index);
    }

    【讨论】:

      【解决方案2】:

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

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

      【讨论】:

        【解决方案3】:

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

        您可以在数组文字和Array.prototype.entries() 方法中利用生成器函数调用之前的展开元素

        function* myGen() {
          let i = 0;
          while (i < 10) {
            i += 1;
            yield i;
          }
        }
        
        for (const [index, value] of [...myGen()].entries()) {
          console.log(index, value);
        }

        【讨论】:

          【解决方案4】:

          一种稍微不同的方法可能是使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}`)
          }

          【讨论】:

          • 没有充分的理由不用生成器语法来定义[Symbol.iterator]...
          • 这是一个很好的观点,感谢@Bergi — 只是太专注于它而没有注意到……已编辑。
          猜你喜欢
          • 1970-01-01
          • 2016-09-13
          • 2015-02-10
          • 2021-05-19
          • 1970-01-01
          • 2021-07-03
          • 2018-05-16
          • 2015-07-19
          相关资源
          最近更新 更多