/*--------------------------------------------------------------------------------------------- * 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 IterableIterator { 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 from(iterable: Iterable | undefined | null): Iterable { return iterable || _empty; } 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) => boolean): boolean { for (const element of iterable) { if (predicate(element)) { return true; } } return false; } export function find(iterable: Iterable, predicate: (t: T) => t is R): T | 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) => R): Iterable { for (const element of iterable) { yield fn(element); } } export function* concat(...iterables: Iterable[]): Iterable { for (const iterable of iterables) { for (const element of iterable) { yield element; } } } export function* concatNested(iterables: Iterable>): Iterable { for (const iterable of iterables) { for (const element of iterable) { yield element; } } } 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 < 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; } }]; } /** * Returns whether the iterables are the same length and all items are * equal using the comparator function. */ export function equals(a: Iterable, b: Iterable, comparator = (at: T, bt: T) => at === bt) { const ai = a[Symbol.iterator](); const bi = b[Symbol.iterator](); while (true) { const an = ai.next(); const bn = bi.next(); if (an.done !== bn.done) { return false; } else if (an.done) { return true; } else if (!comparator(an.value, bn.value)) { return false; } } } }