/*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export namespace Iterable { export function is(thing: any): thing is Iterable { return thing && typeof thing === 'object' && typeof thing[Symbol.iterator] === 'function'; } const _empty: Iterable = Object.freeze([]); export function empty(): Iterable { return _empty; } export function* single(element: T): Iterable { yield element; } export function wrap(iterableOrElement: Iterable | T): Iterable { if (is(iterableOrElement)) { return iterableOrElement; } else { return single(iterableOrElement); } } export function from(iterable: Iterable | undefined | null): Iterable { return iterable || _empty; } export function* reverse(array: Array): Iterable { for (let i = array.length - 1; i >= 0; i--) { yield array[i]; } } export function isEmpty(iterable: Iterable | undefined | null): boolean { return !iterable || iterable[Symbol.iterator]().next().done === true; } export function first(iterable: Iterable): T | undefined { return iterable[Symbol.iterator]().next().value; } export function some(iterable: Iterable, predicate: (t: T, i: number) => unknown): boolean { let i = 0; for (const element of iterable) { if (predicate(element, i++)) { return true; } } return false; } export function find(iterable: Iterable, predicate: (t: T) => t is R): R | undefined; export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined; export function find(iterable: Iterable, predicate: (t: T) => boolean): T | undefined { for (const element of iterable) { if (predicate(element)) { return element; } } return undefined; } export function filter(iterable: Iterable, predicate: (t: T) => t is R): Iterable; export function filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable; export function* filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable { for (const element of iterable) { if (predicate(element)) { yield element; } } } export function* map(iterable: Iterable, fn: (t: T, index: number) => R): Iterable { let index = 0; for (const element of iterable) { yield fn(element, index++); } } export function* flatMap(iterable: Iterable, fn: (t: T, index: number) => Iterable): Iterable { let index = 0; for (const element of iterable) { yield* fn(element, index++); } } export function* concat(...iterables: Iterable[]): Iterable { for (const iterable of iterables) { yield* iterable; } } export function reduce(iterable: Iterable, reducer: (previousValue: R, currentValue: T) => R, initialValue: R): R { let value = initialValue; for (const element of iterable) { value = reducer(value, element); } return value; } /** * Returns an iterable slice of the array, with the same semantics as `array.slice()`. */ export function* slice(arr: ReadonlyArray, from: number, to = arr.length): Iterable { if (from < -arr.length) { from = 0; } if (from < 0) { from += arr.length; } if (to < 0) { to += arr.length; } else if (to > arr.length) { to = arr.length; } for (; from < to; from++) { yield arr[from]; } } /** * Consumes `atMost` elements from iterable and returns the consumed elements, * and an iterable for the rest of the elements. */ export function consume(iterable: Iterable, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable] { const consumed: T[] = []; if (atMost === 0) { return [consumed, iterable]; } const iterator = iterable[Symbol.iterator](); for (let i = 0; i < atMost; i++) { const next = iterator.next(); if (next.done) { return [consumed, Iterable.empty()]; } consumed.push(next.value); } return [consumed, { [Symbol.iterator]() { return iterator; } }]; } export async function asyncToArray(iterable: AsyncIterable): Promise { const result: T[] = []; for await (const item of iterable) { result.push(item); } return Promise.resolve(result); } } export interface IIterator { next(): T; } export class ArrayIterator implements IIterator { private items: T[]; protected start: number; protected end: number; protected index: number; constructor(items: T[], start: number = 0, end: number = items.length) { this.items = items; this.start = start; this.end = end; this.index = start - 1; } public first(): T { this.index = this.start; return this.current(); } public next(): T { this.index = Math.min(this.index + 1, this.end); return this.current(); } protected current(): T { 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: T[], start: number = 0, end: number = items.length) { super(items, start, end); } public current(): T { return super.current(); } public previous(): T { this.index = Math.max(this.index - 1, this.start - 1); return this.current(); } public first(): T { this.index = this.start; return this.current(); } public last(): T { this.index = this.end - 1; return this.current(); } public parent(): T { return null; } } export class MappedIterator implements IIterator { constructor(protected iterator: IIterator, protected fn: (item: T) => R) { // noop } next() { return this.fn(this.iterator.next()); } } export interface INavigator extends IIterator { current(): T; previous(): T; parent(): T; first(): T; last(): T; next(): T; } export class MappedNavigator extends MappedIterator implements INavigator { constructor(protected navigator: INavigator, fn: (item: T) => 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()); } }