在Typescript中迭代的多次遍历

时间:2018-01-23 15:21:09

标签: typescript iterable

在Typescript中处理Iterable多次遍历的好方法是什么?

通常,Iterable只能遍历一次,之后不会产生新元素。例如,IterableIterator是具有此属性的Iterable。因此,一个多次遍历序列的解决方案是首先将其复制到一个数组(比方说)。

Iterable已经多次遍历时,例如使用Array,这种复制是多余的。因此,作为优化,创建possiblyCopyForMultipleTraversals()函数似乎是值得的,该函数仅在必要时返回副本。此函数的保守实现将检测标准集合,如数组和集合,并避免它们的副本。一般可以实现这样的功能吗?

2 个答案:

答案 0 :(得分:1)

这并不一定完美,但是按照"自己创造一个":

如果对象表示MultipleIterable,其迭代器方法生成" fresh&#34,则可以创建一个true接口,该接口只公开值为Iterable的属性。 ;迭代器每次被调用时:

export interface MultipleIterable<T> extends Iterable<T> {
  multipleIterable: true;
}

除了可以在Iterable上调用的功能,还可以检查它是否也是MultipleIterable

function isMultableIterable<T>(iterable: Iterable<T>): iterable is MultipleIterable<T> {
  return (iterable) && ((iterable as any).multipleIterable === true);
}

然后,如果您愿意,可以增加Array的原型来表示数组是MultipleIterable

declare global {
  interface Array<T> {
    multipleIterable: true;
  }
}
Array.prototype.multipleIterable = true;

类似地,您可以修改任何以这种方式运行的内置迭代的原型。

然后,您可以创建一个ReplayableIterable<T>类,其构造函数接受任何Iterable<T>并以这样的方式包装它,使其迭代器始终是新的:

class ReplayableIterable<T> implements MultipleIterable<T> {

  multipleIterable = true as true;

  [Symbol.iterator](): Iterator<T> {
    let cur = 0;
    let iterable = this;
    return {
      next(): IteratorResult<T> {
        while (cur >= iterable.iteratorResults.length) {
          iterable.iteratorResults.push(iterable.iterator.next());
        }
        const ret: IteratorResult<T> = iterable.iteratorResults[cur];
        cur++;
        return ret;
      }
    }
  }

  private iterator: Iterator<T>;
  private iteratorResults: Array<IteratorResult<T>>;

  constructor(iterable: Iterable<T>) {
    this.iterator = iterable[Symbol.iterator]();
    this.iteratorResults = [];
  }

}

这个想法是传入的iterable用于获取一个迭代器,处理迭代器的结果存储在一个数组中。由ReplayableIterable发出的迭代器只有在耗尽数组内容时才会查询实际的迭代器。这是 lazily 复制迭代器:如果传入的iterable有十亿个元素(或更糟,无限),但你只打算遍历其中的一些,你不必采取复制你永远不会使用的元素的记忆和时间(永远不会终止的生成器的命中时间确实相当大)。

最后,导出一个函数,将任何Iterable转换为MultipleIterable,方法是将参数保持不变(如果你给它开始MultipleIterable)或者构建一个ReplayableIterable基于此。

export function toMultipleIterable<T>(iterable: Iterable<T>): MultipleIterable<T> {
  return isMultableIterable(iterable) ? iterable : new ReplayableIterable(iterable);
}

希望有所帮助;祝你好运!

答案 1 :(得分:0)

也许这是要走的路:

type FIterable<T> = (() => Iterable<T>) | Iterable<T>;
export function* run<T = any>(...its: FIterable<T>[]) {
    while (true) {
        for (const it of its) {
            if (typeof it === 'function') {
                yield* it(); // this throw a type error but compile :)
            } else {
                yield* it;
            }
        }
    }
}

// Example:
export const map: Map<number, number> = new Map([[1, 2], [3, 4]]);
const a = run(() => map.keys(), [5, 6, 7], new Set([8, 9]));
for (const _ of Array(10)) {
    console.log(a.next());
};

JS / TS生成器可以使用yield*迭代生成器。 单向迭代的问题应该用函数解决,为什么不再创建一个迭代?

在此示例中,只能重复地图。

我的tsconfig.json

{
    "compilerOptions": {
        "module": "UMD",
        "target": "es5",
        "lib": [
            "es2015",
            "es2015.iterable",
            "dom"
        ]
    },
    "files": [
        "app.ts"
    ]
}