/** * Copyright (c) 2017 mol* contributors, licensed under MIT, See LICENSE file for more info. * * @author David Sehnal */ /** * "Idiomatic" usage: * * const it = ...; * while (it.hasNext) { const v = it.move(); ... } */ interface Iterator { readonly hasNext: boolean, move(): T } class ArrayIteratorImpl implements Iterator { private xs: ArrayLike = []; private index: number = -1; private length: number = 0; private lastValue: T; hasNext: boolean = false; move() { ++this.index; this.lastValue = this.xs[this.index]; this.hasNext = this.index < this.length - 1; return this.lastValue; } constructor(xs: ArrayLike) { this.length = xs.length; this.hasNext = xs.length > 0; this.xs = xs; this.index = -1; // try to avoid deoptimization with undefined values this.lastValue = xs.length > 0 ? xs[0] : void 0 as any; } } class RangeIteratorImpl implements Iterator { private value: number = 0; hasNext: boolean = false; move() { ++this.value; this.hasNext = this.value < this.max; return this.value; } constructor(min: number, private max: number) { this.value = min - 1; this.hasNext = max >= min; } } class ValueIterator implements Iterator { hasNext = true; move() { this.hasNext = false; return this.value; } constructor(private value: T) { } } class MapIteratorImpl implements Iterator { hasNext: boolean = false; move() { const v = this.f(this.base.move()); this.hasNext = this.base.hasNext; return v; } constructor(private base: Iterator, private f: (v: T) => R) { this.hasNext = base.hasNext; } } class FilterIteratorImpl implements Iterator { private next: T; hasNext: boolean; move() { const ret = this.next; this.hasNext = this.findNext(); return ret; } private findNext() { while (this.base.hasNext) { this.next = this.base.move(); if (this.p(this.next)) return true; } return false; } constructor(private base: Iterator, private p: (v: T) => boolean) { this.hasNext = this.findNext(); } } namespace Iterator { export const Empty: Iterator = new RangeIteratorImpl(0, -1); export function Array(xs: ArrayLike): Iterator { return new ArrayIteratorImpl(xs); } export function Value(value: T): Iterator { return new ValueIterator(value); } export function Range(min: number, max: number): Iterator { return new RangeIteratorImpl(min, max); } export function map(base: Iterator, f: (v: T) => R): Iterator { return new MapIteratorImpl(base, f); } export function filter(base: Iterator, p: (v: T) => boolean): Iterator { return new FilterIteratorImpl(base, p); } // Iterate until first truthy value is returned. export function forEach(it: Iterator, f: (v: T, ctx: Ctx) => any, ctx: Ctx): Ctx { while (it.hasNext) { const c = f(it.move(), ctx); if (c) return ctx; } return ctx; } } export { Iterator };