/** * ```js * import from from '@adamburgess/linq' * const sequence = from(['an', 'iterable', 'here!']); * // now use all the methods on sequence! * ``` * * See {@link AnySequence} for all the transformations you can do to a sequence. * @module */ /** A sequence of values. * * To create me, use from: * ```js * const sequence = from(['an', 'iterable', 'here!']); * // now use all the methods on sequence! * ``` */ export interface AnySequence extends Iterable { /** Map each element to another * * ```js * from([2, 1, 5]).map(x => x + 1) * // => [3, 2, 6] * ``` */ map(f: (arg: T, index: number) => TResult): Sequence; /** Filters with a TS type assertion ('is'), narrowing to that type * * ```typescript * function isNumber(x: number | string): x is number { * return typeof x === 'number'; * } * from(['string', 12, 'str']).where(isNumber) * // => [12] * // sequence is now Sequence * ``` */ where(f: (arg: T | TNarrowed, index: number) => arg is TNarrowed): Sequence; /** Filters with and narrows to a type * * ```typescript * from(['string', 12, 'str']).where(x => typeof x === 'number') * // => [12] * // sequence is now Sequence * ``` * note! must use generic type! otherwise this overload is not used, and the type is not narrowed. */ where(f: (arg: T | TNarrowed, index: number) => boolean): Sequence; /** Filters with a predicate that must return a truthy value * * ```js * from([5, 10, 20]).where(x => x >= 8) * // => [10, 20] * ``` */ where(f: (arg: T, index: number) => any): Sequence; /** Reverses the sequence. * * ```js * from([2, 1, 5]).reverse() * // => [5, 1, 2] * ``` */ reverse(): Sequence; /** Project each element to get a key, and group all items by that key. * * ```js * from([10, 15, 20]).groupBy(x => Math.trunc(x / 10)) * .map(g => ({ key: g.key, values: Array.from(g) })) * // => [{ key: 1, values: [10, 15] }, * // { key: 2, values: [20] }] * ``` */ groupBy(keySelector: (arg: T) => TKey): ArraySequence>; /** Project each element to get a key, and group all items, each projected onto another type. * * ```js * from([10, 15, 20]).groupBy(x => Math.trunc(x / 10), x => x.toString()) * .map(g => ({ key: g.key, values: Array.from(g) })) * // => [{ key: 1, values: ['10', '15'] }, * // { key: 2, values: ['20'] }] * ``` */ groupBy(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TProject): ArraySequence>; /** Sort the array in ascending order of the selector * * ```js * from([4, 1, 10]).orderBy(x => x) * // => [1, 4, 10] * ``` */ orderBy(keySelector: (arg: T) => string | number): OrderedSequence; /** Sort the array in ascending order of the selector, with a custom comparer * * ```js * // Sort alphabetically, ignoring case. * from(['A xylophone', 'a frog', 'a zoo']).orderBy(x => x, new Intl.Collator('en', { sensitivity: 'base' }).compare) * // => ['a frog', 'A xylophone', 'a zoo'] * ``` */ orderBy(keySelector: (arg: T) => TKey, comparer: ICompare): OrderedSequence; /** Sort the array in descending order of the selector * * ```js * from([4, 1, 10]).orderByDescending(x => x) * // => [10, 4, 1] * ``` */ orderByDescending(keySelector: (arg: T) => string | number): OrderedSequence; /** Sort the array in descending order of the selector, with a custom comparer * * ```js * // Sort reverse alphabetically, ignoring case. * from(['A xylophone', 'a frog', 'a zoo']).orderByDescending(x => x, new Intl.Collator('en', { sensitivity: 'base' }).compare) * // => ['a zoo', 'A xylophone', 'a frog'] * ``` */ orderByDescending(keySelector: (arg: T) => TKey, comparer: ICompare): OrderedSequence; /** Take a maximum amount of elements * * ```js * from([2, 1, 5]).take(2) * // => [2, 1] * ``` */ take(count: number): Sequence; /** Take elements while the predicate is true, then skips the rest * * ```js * from([2, 3, 1, 5]).takeWhile(x => x >= 2) * // => [2, 3] * ``` */ takeWhile(predicate: (arg: T) => any): Sequence; /** Skip a number of elements before letting the rest through * * ```js * from([2, 3, 1, 5]).skip(2) * // => [1, 5] * ``` */ skip(count: number): Sequence; /** Skip elements while the predicate is true, then take the rest * * ```js * from([2, 3, 1, 5]).skipWhile(x => x >= 2) * // => [1, 5] * ``` */ skipWhile(predicate: (arg: T) => any): Sequence; /** Append another iterable to the end * * ```js * from([1, 2]).append([3, 4]) * // => [1, 2, 3, 4] * ``` */ append(iterable: Iterable): Sequence; /** Prepend another iterable to the start * * ```js * from([1, 2]).prepend([3, 4]) * // => [3, 4, 1, 2] * ``` */ prepend(iterable: Iterable): Sequence; /** Get all distinct elements of the sequence using strict equality. First value wins. * * ```js * from([1, 2, 2, 3]).distinct() * // => [1, 2, 3] * ``` */ distinct(): Sequence; /** Get all distinct elements of the sequence, mapping each element to a key that will be used for strict equality. First value wins. * * ```js * from([10, 15, 20, 25, 30]).distinct(x => Math.trunc(x / 10)) * // => [10, 20, 30] * ``` */ distinct(keySelector: (arg: T) => unknown): Sequence; /** Project each element to an iterable/array, then flatten the result * * ```js * from(['1 2', '3 4']).flat(x => x.split(' ')) * // => ['1', '2', '3', '4'] * ``` */ flat(projector: (input: T) => Iterable): Sequence; /** Correlates the elements of two sequences based on matching keys. An inner join. * * ```js * const appleTypes = [ * { name: 'green apple', id: 5 }, * { name: 'red apple', id: 2 }, * { name: 'yellow apple', id: 10 } * ]; * const apples = [ * { name: 'golden delicious', type: 10 }, * { name: 'granny smith', type: 5 }, * { name: 'pink lady', type: 2 }, * { name: 'fuji', type: 2 }, * { name: 'unknown', type: 999 } * ]; * * from(apples).join( * appleTypes, * apple => apple.type, * type => type.id, * (apple, type) => `${apple.name}: ${type.name}` * ) * // => [ 'golden delicious: yellow apple', * // 'granny smith: green apple', * // 'pink lady: red apple', * // 'fuji: red apple' ] * ``` */ join(innerSequence: Iterable, outerKeySelector: (arg: T) => TKey, innerKeySelector: (arg: TInner) => TKey, resultSelector: (outer: T, inner: TInner) => TResult): Sequence; /** Correlates the elements of two sequences based on matching keys, and groups everything in the other table. * * ```js * const appleTypes = [ * { name: 'green apple', id: 5 }, * { name: 'red apple', id: 2 }, * { name: 'yellow apple', id: 10 } * ]; * const apples = [ * { name: 'golden delicious', type: 10 }, * { name: 'granny smith', type: 5 }, * { name: 'pink lady', type: 2 }, * { name: 'fuji', type: 2 }, * { name: 'unknown', type: 999 } * ]; * * from(appleTypes).groupJoin( * apples, * type => type.id, * apple => apple.type, * (type, apples) => `${type.name}: ${apples.map(a => a.name).joinString(', ')}` * ); * // => [ 'green apple: granny smith', * // 'red apple: pink lady, fuji', * // 'yellow apple: golden delicious' ] * ``` */ groupJoin(innerSequence: Iterable, outerKeySelector: (arg: T) => TKey, innerKeySelector: (arg: TInner) => TKey, resultSelector: (outer: T, inner: Sequence) => TResult): Sequence; /** Counts the number of elements in the sequence * * ```js * from([2, 1, 5]).count() * // => 3 * from([]).count() * // => 0 * ``` */ count(): number; /** * Converts this sequence to an array. * Note: If you are using this Sequence in a for..of loop, you do _not_ need this -- * you can use the sequence directly. * * ```js * from([2, 1, 5]).toArray() * // => [2, 1, 5] * ``` */ toArray(): T[]; /** Converts this sequence to a Map. First key wins. * * ```js * from([{ k: 'a', v: 123 }, { k: 'b', v: 456 }]).toMap(x => x.k, x => x.v) * // => new Map([['a', 123], ['b', 456]]) * ``` */ toMap(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TElement): Map; /** Converts this sequence into an object. First key wins. * * ```js * from([{ k: 'a', v: 123 }, { k: 'b', v: 456 }]).toObject(x => x.k, x => x.v) * // => { a: 123, b: 456 } * ``` */ toObject(keySelector: (arg: T) => TKey, elementSelector: (arg: T) => TElement): Record; /** Convert this sequence into a Set * * ```js * from([2, 1, 1, 5]).toSet() * // => new Set([2, 1, 5]) * ``` */ toSet(): Set; /** Map each element and convert the resulting sequence into a set * * ```js * from([2, 1, 1, 5]).toSet(x => x + 1) * // => new Set([3, 2, 6]) * ``` */ toSet(projector: (arg: T) => TProject): Set; /** Get the first element in the sequence. Will throw if empty! Use firstOrDefault if no throw is wanted. * * ```js * from([2, 1, 5]).first() * // => 2 * from([]).first() * // => throws * ``` */ first(): T; /** Get the first element in the sequence that matches a condition. Will throw if empty! Use firstOrDefault if no throw is wanted. * * ```js * from([2, 1, 5]).first(x => x >= 4) * // => 5 * from([2, 1, 5]).first(x => x < 0) * // => throws * ``` */ first(predicate: (arg: T) => any): T; /** Get the first element in the sequence. If empty, returns undefined. * * ```js * from([2, 1, 5]).firstOrDefault() * // => 2 * from([]).firstOrDefault() * // => undefined * ``` */ firstOrDefault(): T | undefined; /** Get the first element in the sequence that matches a condition. If empty or no matches, returns undefined. * * ```js * from([2, 1, 5]).firstOrDefault(x => x >= 4) * // => 5 * from([2, 1, 5]).firstOrDefault(x => x < 0) * // => undefined * ``` */ firstOrDefault(predicate: (arg: T) => any): T | undefined; /** Get the _only_ element in the sequence. Will throw if empty or more than one element! Use singleOrDefault if no throw is wanted. * * ```js * from([2]).single() * // => 2 * from([2, 1, 5]).single() * // => throws * from([]).single() * // => throws * ``` */ single(): T; /** Get the _only_ element in the sequence that matches a condition. Will throw if empty or more than one element! Use singleOrDefault if no throw is wanted. * * ```js * from([2, 1, 5]).single(x => x >= 4) * // => 5 * from([2, 1, 5]).single(x => x >= 1) * // => throws * ``` */ single(predicate: (arg: T) => any): T; /** Get the _only_ element in the sequence. Returns undefined if empty or more than one element. * * ```js * from([2]).singleOrDefault() * // => 2 * from([2, 1, 5]).singleOrDefault() * // => undefined * ``` */ singleOrDefault(): T | undefined; /** Get the _only_ element in the sequence that matches a condition. Returns undefined if empty or more than one element. * * ```js * from([2, 1, 5]).singleOrDefault(x => x >= 4) * // => 5 * from([2, 1, 5]).singleOrDefault(x => x >= 1) * // => undefined * ``` */ singleOrDefault(predicate: (arg: T) => any): T | undefined; /** Get the last element in the sequence. Will throw if empty! Use lastOrDefault if no throw is wanted. * * ```js * from([2, 1, 5]).last() * // => 5 * from([]).last() * // => throws * ``` */ last(): T; /** Get the last element in the sequence that matches a condition. Will throw if empty! Use lastOrDefault if no throw is wanted. * * ```js * from([2, 1, 5]).last(x => x < 4) * // => 1 * from([]).last(x => x < 0) * // => throws * ``` */ last(predicate: (arg: T) => any): T; /** Get the last element in the sequence. If empty, returns undefined. * * ```js * from([2, 1, 5]).lastOrDefault() * // => 5 * from([]).lastOrDefault() * // => undefined * ``` */ lastOrDefault(): T | undefined; /** Get the last element in the sequence that matches a condition. If empty or no matches, returns undefined. * * ```js * from([2, 1, 5]).lastOrDefault(x => x < 4) * // => 1 * from([2, 1, 5]).lastOrDefault(x => x < 0) * // => undefined * ``` */ lastOrDefault(predicate: (arg: T) => any): T | undefined; /** True if all elements pass the predicate * * ```js * from([2, 1, 5]).all(x => x >= 1) * // => true * from([2, 1, 5]).all(x => x >= 2) * // => false * ``` */ all(predicate: (arg: T) => any): boolean; /** True if any elements pass the predicate * * ```js * from([2, 1, 5]).any(x => x >= 4) * // => true * from([2, 1, 5]).any(x => x <= 0) * // => false * ``` */ any(predicate: (arg: T) => any): boolean; /** True if the sequence is not empty * * ```js * from([2, 1, 5]).any() * // => true * from([]).any() * // => false * ``` */ any(): boolean; /** True if no elements pass the predicate * * ```js * from([2, 1, 5]).none(x => x === 0) * // => true * from([2, 1, 5]).none(x => x === 1) * // => false * ``` */ none(predicate: (arg: T) => any): boolean; /** True if the element is in the sequence. Checked with ===. * * ```js * from([2, 1, 5]).contains(1) * // => true * from([{ a: '1' }]).contains({ a: '1' }) * // => false (strict equality. use any with a custom search instead.) * ``` */ contains(value: T): boolean; /** Projects each element to a number and sums the sequence. If empty, returns 0. * * ```js * from(['2', '1', '5']).sum(x => parseFloat(x)) * // => 8 * ``` */ sum(f: (arg: T) => number): number; /** Projects each element to a number and averages the sequence. If empty, throws. * * ```js * from(['2', '1', '6']).average(x => parseFloat(x)) * // => 3 * ``` */ average(f: (arg: T) => number): number; /** Projects each element to a number and finds the max of the sequence. If empty, throws. * * ```js * from(['2', '1', '5']).max(x => parseFloat(x)) * // => 5 * ``` */ max(f: (arg: T) => number): number; /** Projects each element to a number and finds the min of the sequence. If empty, throws. * * ```js * from(['2', '1', '5']).min(x => parseFloat(x)) * // => 1 * ``` */ min(f: (arg: T) => number): number; /** Finds the minimum element in the sequence according to a selector. Equivalent (but faster) to orderBy(f).first(). * * ```js * from([{ key: 'A', value: 2 }, { key: 'B', value: 1 }, { key: 'C', value: 10 }]).minBy(x => x.value) * // => { key: 'B', value: 1 } * ``` */ minBy(f: (arg: T) => number): T; /** Finds the maximum element in the sequence according to a selector. Equivalent (but faster) to orderByDescending(f).first(). * * ```js * from([{ key: 'A', value: 2 }, { key: 'B', value: 1 }, { key: 'C', value: 10 }]).maxBy(x => x.value) * // => { key: 'C', value: 10 } * ``` */ maxBy(f: (arg: T) => number): T; /** Iterates through this sequence. */ [Symbol.iterator](): Iterator; } /** A sequence of numbers */ export interface NumberSequence extends AnySequence { /** Sums every number in the sequence. If empty, returns 0. * * ```js * from([2, 1, 5]).sum() * // => 8 * ``` */ sum(): number; /** Averages the sequence. If empty, throws. * * ```js * from([2, 1, 6]).average() * // => 3 * ``` */ average(): number; /** Finds the maximum in the sequence. If empty, throws. * * ```js * from([2, 1, 5]).max() * // => 5 * ``` */ max(): number; /** Finds the minimum in the sequence. If empty, throws. * * ```js * from([2, 1, 5]).min() * // => 1 * ``` */ min(): number; } declare type UnwrapIterable = [T] extends [Iterable] ? U : never; /** A sequence of iterable elements */ export interface ArraySequence extends AnySequence { /** Project each element to an iterable/array, then flatten the result * * ```js * from([[1, 2], [3, 4]]).flat(x => [...x, 0]) * // => [1, 2, 0, 3, 4, 0] * ``` */ flat(projector: (input: T) => Iterable): Sequence; /** Flatten the sequence * * ```js * from([[1, 2], [3, 4]]).flat() * // => [1, 2, 3, 4] * ``` */ flat(): Sequence>; } /** A sequence of strings */ export interface StringSequence extends AnySequence { /** Joins the elements with an optional separator * * ```js * from(['a', 'b', 'c']).joinString(', ') * // => 'a, b, c' * ``` */ joinString(separator?: string): string; } export interface WithKey { /** The key that this set was grouped by */ readonly key: TKey; } export interface WithOrderedMethods { /** Order the sequence by another key, ascending. * * ```js * // Sort by length of the string, then alphabetical order * from(['two', 'one', 'thirteen', 'five']).orderBy(x => x.length).thenBy(x => x) * // => ['one', 'two', 'five', 'thirteen'] * ``` */ thenBy(keySelector: (arg: T) => string | number): OrderedSequence; /** Order the sequence by another key, ascending, with a custom comparer. * * See orderBy for an example. */ thenBy(keySelector: (arg: T) => TKey, comparer: ICompare): OrderedSequence; /** Order the sequence by another key, descending. * * ```js * // Sort by length of the string, then reverse alphabetical order * from(['one', 'two', 'thirteen', 'five']).orderBy(x => x.length).thenByDescending(x => x) * // => ['two', 'one', 'five', 'thirteen'] * ``` */ thenByDescending(keySelector: (arg: T) => string | number): OrderedSequence; /** Order the sequence by another key, descending, with a custom comparer. * * * See orderBy for an example. */ thenByDescending(keySelector: (arg: T) => TKey, comparer: ICompare): OrderedSequence; } declare type DoesExtend = [T] extends [TWant] ? T : never; declare type DoesExtendAny = any extends T ? never : T; declare type ExtendsCarefully = DoesExtendAny>; /** A sequence of values. */ export declare type Sequence = ExtendsCarefully extends never ? (ExtendsCarefully extends never ? (ExtendsCarefully> extends never ? AnySequence : ArraySequence) : StringSequence) : NumberSequence; /** A sequence with a key. Obtained from groupBy. */ export declare type KeySequence = WithKey & Sequence; /** An ordered sequence. Can use thenBy to continue ordering. */ export declare type OrderedSequence = WithOrderedMethods & Sequence; /** Gets the sequence type. * ```typescript * type MySequence = Sequence * type MySequenceType = SequenceType * // MySequenceType === string * ``` */ export declare type SequenceType = T extends Sequence ? Y : never; /** Compares two values. * If a comes before b, the result should be negative. * If both values are equal, the result should be zero. * If a comes after b, the result should be positive. */ export declare type ICompare = (a: T, b: T) => number; /** Start the sequence. Can be used with an array or any iterable. * * ```js * // Import me: * import from from '@adamburgess/linq' * // Arrays: * from([1, 2, 3]) * // Objects that support the Iterable protocol: * from(new Map()) * // Generators: * from(function*() { yield 'generated values' }) * ``` */ export declare function from(it: Iterable): Sequence; export default from;