/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export interface IteratorDefinedResult { readonly done: false; readonly value: T; } export interface IteratorUndefinedResult { readonly done: true; readonly value: undefined; } export const FIN: IteratorUndefinedResult = { done: true, value: undefined }; export type IteratorResult = IteratorDefinedResult | IteratorUndefinedResult; export interface Iterator { next(): IteratorResult; } interface NativeIteratorYieldResult { done?: false; value: TYield; } interface NativeIteratorReturnResult { done: true; value: TReturn; } type NativeIteratorResult = NativeIteratorYieldResult | NativeIteratorReturnResult; export interface NativeIterator { next(): NativeIteratorResult; } export module Iterator { const _empty: Iterator = { next() { return FIN; } }; export function empty(): Iterator { return _empty; } export function single(value: T): Iterator { let done = false; return { next(): IteratorResult { if (done) { return FIN; } done = true; return { done: false, value }; } }; } export function fromArray(array: ReadonlyArray, index = 0, length = array.length): Iterator { return { next(): IteratorResult { if (index >= length) { return FIN; } return { done: false, value: array[index++] }; } }; } export function fromNativeIterator(it: NativeIterator): Iterator { return { next(): IteratorResult { const result = it.next(); if (result.done) { return FIN; } return { done: false, value: result.value }; } }; } export function from(elements: Iterator | T[] | undefined): Iterator { if (!elements) { return Iterator.empty(); } else if (Array.isArray(elements)) { return Iterator.fromArray(elements); } else { return elements; } } export function map(iterator: Iterator, fn: (t: T) => R): Iterator { return { next() { const element = iterator.next(); if (element.done) { return FIN; } else { return { done: false, value: fn(element.value) }; } } }; } export function filter(iterator: Iterator, fn: (t: T) => boolean): Iterator { return { next() { while (true) { const element = iterator.next(); if (element.done) { return FIN; } if (fn(element.value)) { return { done: false, value: element.value }; } } } }; } export function forEach(iterator: Iterator, fn: (t: T) => void): void { for (let next = iterator.next(); !next.done; next = iterator.next()) { fn(next.value); } } export function collect(iterator: Iterator, atMost: number = Number.POSITIVE_INFINITY): T[] { const result: T[] = []; if (atMost === 0) { return result; } let i = 0; for (let next = iterator.next(); !next.done; next = iterator.next()) { result.push(next.value); if (++i >= atMost) { break; } } return result; } export function concat(...iterators: Iterator[]): Iterator { let i = 0; return { next() { if (i >= iterators.length) { return FIN; } const iterator = iterators[i]; const result = iterator.next(); if (result.done) { i++; return this.next(); } return result; } }; } } export type ISequence = Iterator | T[]; export function getSequenceIterator(arg: Iterator | T[]): Iterator { if (Array.isArray(arg)) { return Iterator.fromArray(arg); } else { return arg; } } export interface INextIterator { next(): T | null; } export class ArrayIterator implements INextIterator { private readonly items: readonly T[]; protected start: number; protected end: number; protected index: number; constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { this.items = items; this.start = start; this.end = end; this.index = index; } public first(): T | null { this.index = this.start; return this.current(); } public next(): T | null { this.index = Math.min(this.index + 1, this.end); return this.current(); } protected current(): T | null { if (this.index === this.start - 1 || this.index === this.end) { return null; } return this.items[this.index]; } } export class ArrayNavigator extends ArrayIterator implements INavigator { constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { super(items, start, end, index); } public current(): T | null { return super.current(); } public previous(): T | null { this.index = Math.max(this.index - 1, this.start - 1); return this.current(); } public first(): T | null { this.index = this.start; return this.current(); } public last(): T | null { this.index = this.end - 1; return this.current(); } public parent(): T | null { return null; } } export class MappedIterator implements INextIterator { constructor(protected iterator: INextIterator, protected fn: (item: T | null) => R) { // noop } next() { return this.fn(this.iterator.next()); } } export interface INavigator extends INextIterator { current(): T | null; previous(): T | null; parent(): T | null; first(): T | null; last(): T | null; next(): T | null; } export class MappedNavigator extends MappedIterator implements INavigator { constructor(protected navigator: INavigator, fn: (item: T | null) => R) { super(navigator, fn); } current() { return this.fn(this.navigator.current()); } previous() { return this.fn(this.navigator.previous()); } parent() { return this.fn(this.navigator.parent()); } first() { return this.fn(this.navigator.first()); } last() { return this.fn(this.navigator.last()); } next() { return this.fn(this.navigator.next()); } }