/** * Copyright (c) OpenLens Authors. All rights reserved. * Licensed under MIT License. See LICENSE in root directory for more information. */ export type Falsey = false | 0 | "" | null | undefined; /** * Create a new type safe empty Iterable * @returns An `Iterable` that yields 0 items */ // eslint-disable-next-line require-yield export function* newEmpty(): IterableIterator { return; } /** * Creates a new `Iterable` that yields at most n items from src. * Does not modify `src` which can be used later. * @param src An initial iterator * @param n The maximum number of elements to take from src. Yields up to the floor of `n` and 0 items if n < 0 */ export function* take(src: Iterable, n: number): IterableIterator { outer: for (let i = 0; i < n; i += 1) { for (const item of src) { yield item; continue outer; } // if we are here that means that `src` has been exhausted. Don't bother trying again. break outer; } } /** * Creates a new iterator that iterates (lazily) over its input and yields the * result of `fn` for each item. * @param src A type that can be iterated over * @param fn The function that is called for each value */ export function* map(src: Iterable, fn: (from: T) => U): IterableIterator { for (const from of src) { yield fn(from); } } /** * The single layer flattening of an iterator, discarding `Falsey` values. * @param src A type that can be iterated over * @param fn The function that returns either an iterable over items that should be filtered out or a `Falsey` value indicating that it should be ignored */ export function* filterFlatMap(src: Iterable, fn: (from: T) => Iterable | Falsey): IterableIterator { for (const from of src) { if (!from) { continue; } const mapping = fn(from); if (!mapping) { continue; } for (const mapped of mapping) { if (mapped) { yield mapped; } } } } /** * Returns a new iterator that yields the items that each call to `fn` would produce * @param src A type that can be iterated over * @param fn A function that returns an iterator */ export function* flatMap(src: Iterable, fn: (from: T) => Iterable): IterableIterator { for (const from of src) { yield* fn(from); } } /** * Creates a new iterator that iterates (lazily) over its input and yields the * items that return a `truthy` value from `fn`. * @param src A type that can be iterated over * @param fn The function that is called for each value */ export function* filter(src: Iterable, fn: (from: T) => any): IterableIterator { for (const from of src) { if (fn(from)) { yield from; } } } /** * Creates a new iterator that iterates (lazily) over its input and yields the * result of `fn` when it is `truthy` * @param src A type that can be iterated over * @param fn The function that is called for each value */ export function* filterMap(src: Iterable, fn: (from: T) => U | Falsey): IterableIterator { for (const from of src) { const res = fn(from); if (res) { yield res; } } } /** * Creates a new iterator that iterates (lazily) over its input and yields the * result of `fn` when it is not null or undefined * @param src A type that can be iterated over * @param fn The function that is called for each value */ export function* filterMapStrict(src: Iterable, fn: (from: T) => U | null | undefined): IterableIterator { for (const from of src) { const res = fn(from); if (res != null) { yield res; } } } /** * Iterate through `src` until `match` returns a truthy value * @param src A type that can be iterated over * @param match A function that should return a truthy value for the item that you want to find * @returns The first entry that `match` returns a truthy value for, or `undefined` */ export function find(src: Iterable, match: (i: T) => any): T | undefined { for (const from of src) { if (match(from)) { return from; } } return void 0; } /** * Iterate over `src` calling `reducer` with the previous produced value and the current * yielded value until `src` is exausted. Then return the final value. * @param src The value to iterate over * @param reducer A function for producing the next item from an accumilation and the current item * @param initial The initial value for the iteration */ export function reduce>(src: Iterable, reducer: (acc: R, cur: T) => R, initial: R): R; export function reduce(src: Iterable, reducer: (acc: R, cur: T) => R, initial: R): R { let acc = initial; for (const item of src) { acc = reducer(acc, item); } return acc; } /** * A convenience function for reducing over an iterator of strings and concatenating them together * @param src The value to iterate over * @param connector The string value to intersperse between the yielded values * @returns The concatenated entries of `src` interspersed with copies of `connector` */ export function join(src: Iterable, connector = ","): string { return reduce(src, (acc, cur) => `${acc}${connector}${cur}`, ""); } /** * Returns the next value after iterating over the iterable `index` times. * * For example: `nth(["a", "b"], 0)` will return `"a"` * For example: `nth(["a", "b"], 1)` will return `"b"` * For example: `nth(["a", "b"], 2)` will return `undefined` */ export function nth(src: Iterable, index: number): T | undefined { const iteree = src[Symbol.iterator](); while (index-- > 0) { iteree.next(); } return iteree.next().value; } /** * Iterate through `src` and return `true` if `fn` returns a thruthy value for every yielded value. * Otherwise, return `false`. This function short circuits. * @param src The type to be iterated over * @param fn A function to check each iteration */ export function every(src: Iterable, fn: (val: T) => any): boolean { for (const val of src) { if (!fn(val)) { return false; } } return true; }