@absoluteui/helpers/linq
Lazy LINQ: pipeable operators and a fluent Enumerable with the .NET surface.
import { ... } from '@absoluteui/helpers/linq';How to use
Section titled “How to use”LINQ-style queries over any iterable: filter, project, join, group, and aggregate in one readable chain. Queries are lazy (nothing runs until a terminal such as toArray() or sum()), so they work on generators and large sequences. Use the fluent from(...) style, or the standalone operators with pipe when bundle size matters. For one-off array tasks, helpers/array is simpler; the Helpers and services guide covers the shared conventions.
How do I build a headcount and payroll report per department?
Section titled “How do I build a headcount and payroll report per department?”Join employees to departments, group by department name, and aggregate each group; orderByDescending sorts the report.
import { from } from '@absoluteui/helpers/linq';
const departments = [{ id: 1, name: 'Finance' }, { id: 2, name: 'HR' }, { id: 3, name: 'IT' }];const employees = [ { name: 'Asha', deptId: 1, salary: 5200, active: true }, { name: 'Ben', deptId: 2, salary: 4100, active: true }, { name: 'Chen', deptId: 1, salary: 6100, active: true }, { name: 'Dana', deptId: 3, salary: 7000, active: false },];const report = from(employees) .where((e) => e.active) .join(departments, (e) => e.deptId, (d) => d.id, (e, d) => ({ dept: d.name, salary: e.salary })) .groupBy((r) => r.dept) .select((g) => ({ dept: g.key, headcount: g.count(), payroll: g.sum((r) => r.salary) })) .orderByDescending((r) => r.payroll) .toArray();report; // => [{ dept: 'Finance', headcount: 2, payroll: 11300 }, { dept: 'HR', headcount: 1, payroll: 4100 }]How do I keep rows that have no match?
Section titled “How do I keep rows that have no match?”leftJoin passes undefined for a missing partner, so employees without a manager still appear; groupJoin attaches all matches as a list, for example each manager’s direct reports.
import { from } from '@absoluteui/helpers/linq';
const people = [{ id: 'E-1', name: 'Maya', managerId: null }, { id: 'E-2', name: 'Omar', managerId: 'E-1' }, { id: 'E-3', name: 'Lin', managerId: 'E-1' }];from(people) .leftJoin(people, (p) => p.managerId, (m) => m.id, (p, m) => `${p.name}: ${m?.name ?? 'no manager'}`) .toArray(); // => ['Maya: no manager', 'Omar: Maya', 'Lin: Maya']from(people) .groupJoin(people, (m) => m.id, (p) => p.managerId, (m, reports) => ({ manager: m.name, reports: reports.count() })) .where((r) => r.reports > 0) .toArray(); // => [{ manager: 'Maya', reports: 2 }]How do I stop early on a large or endless sequence?
Section titled “How do I stop early on a large or endless sequence?”Because queries are lazy, take, first, and any stop pulling items as soon as they have an answer; only the items needed are produced.
import { from } from '@absoluteui/helpers/linq';
let produced = 0;function* payDates(): Generator<string> { for (let month = 1; ; month++) { produced++; yield `2026-${String(month).padStart(2, '0')}-25`; }}from(payDates()).skip(9).take(2).toArray(); // => ['2026-10-25', '2026-11-25']produced; // => 11How do I use standalone operators to keep bundles small?
Section titled “How do I use standalone operators to keep bundles small?”pipe applies operator functions left to right; import only the operators you use.
import { orderBy, pipe, select, take, thenBy, toArray, where } from '@absoluteui/helpers/linq';
const requests = [ { who: 'Ben', days: 5, type: 'annual' }, { who: 'Asha', days: 2, type: 'sick' }, { who: 'Chen', days: 5, type: 'annual' }, { who: 'Dana', days: 1, type: 'annual' },];pipe( requests, where((r) => r.type === 'annual'), orderBy((r) => -r.days), thenBy((r) => r.who), take(2), select((r) => r.who), toArray(),); // => ['Ben', 'Chen']What goes wrong?
Section titled “What goes wrong?”first, single, and elementAt throw on missing or ambiguous results; use the ...OrDefault variants when absence is normal.
import { from } from '@absoluteui/helpers/linq';
const ids = from(['E-1', 'E-2']);ids.firstOrDefault((id) => id === 'E-9', 'none'); // => 'none'ids.first((id) => id === 'E-9'); // => throws HLP_LINQ_NO_ELEMENTSids.single(); // => throws HLP_LINQ_MULTIPLE_ELEMENTSAPI reference
Section titled “API reference”Enumerable
Section titled “Enumerable”Functions
Section titled “Functions”empty
An empty sequence.
function empty<T>(): Enumerable<T>;Example:
empty<number>().count(); // => 0from
Wraps any iterable in a lazy, fluent Enumerable.
function from<T>(source: Iterable<T>): Enumerable<T>;Example:
const users = [ { name: 'Ben', email: 'ben@x.io', active: true }, { name: 'Cy', email: 'cy@x.io', active: false }, { name: 'Ada', email: 'ada@x.io', active: true },];from(users).where((u) => u.active).orderBy((u) => u.name).select((u) => u.email).toArray(); // => ['ada@x.io', 'ben@x.io']range
count consecutive integers from start (lazy).
function range(start: number, count: number): Enumerable<number>;Example:
range(1, 3).toArray(); // => [1, 2, 3]repeat
value repeated count times (lazy).
function repeat<T>(value: T, count: number): Enumerable<T>;Example:
repeat('x', 2).toArray(); // => ['x', 'x']Classes
Section titled “Classes”Enumerable
Lazy, re-iterable sequence with the .NET LINQ surface.
class Enumerable<T> implements Iterable<T> { [Symbol.iterator](): Iterator<T>; pipe<A>(op1: (source: Iterable<T>) => A): A; pipe<A, B>(op1: (source: Iterable<T>) => A, op2: (a: A) => B): B; pipe<A, B, C>(op1: (source: Iterable<T>) => A, op2: (a: A) => B, op3: (b: B) => C): C; pipe<A, B, C, D>(op1: (source: Iterable<T>) => A, op2: (a: A) => B, op3: (b: B) => C, op4: (c: C) => D): D; where<S extends T>(predicate: (item: T, index: number) => item is S): Enumerable<S>; where(predicate: Predicate<T>): Enumerable<T>; whereType<S extends T>(guard: (item: T) => item is S): Enumerable<S>; select<U>(selector: (item: T, index: number) => U): Enumerable<U>; selectMany<U>(selector: (item: T, index: number) => Iterable<U>): Enumerable<U>; selectMany<U, R>(selector: (item: T, index: number) => Iterable<U>, result: (item: T, child: U) => R): Enumerable<R>; orderBy<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedEnumerable<T>; orderByDescending<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedEnumerable<T>; reverse(): Enumerable<T>; groupBy<K>(key: (item: T) => K): Enumerable<EnumerableGrouping<K, T>>; groupBy<K, E>(key: (item: T) => K, element: (item: T) => E): Enumerable<EnumerableGrouping<K, E>>; groupBy<K, E, R>(key: (item: T) => K, element: (item: T) => E, result: (key: K, items: Enumerable<E>) => R): Enumerable<R>; join<U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inner: U) => R): Enumerable<R>; groupJoin<U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inners: Enumerable<U>) => R): Enumerable<R>; leftJoin<U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inner: U | undefined) => R): Enumerable<R>; distinct(): Enumerable<T>; distinctBy(key: (item: T) => unknown): Enumerable<T>; union(other: Iterable<T>): Enumerable<T>; unionBy(other: Iterable<T>, key: (item: T) => unknown): Enumerable<T>; intersect(other: Iterable<T>): Enumerable<T>; intersectBy(other: Iterable<T>, key: (item: T) => unknown): Enumerable<T>; except(other: Iterable<T>): Enumerable<T>; exceptBy(other: Iterable<T>, key: (item: T) => unknown): Enumerable<T>; concat(other: Iterable<T>): Enumerable<T>; zip<U>(other: Iterable<U>): Enumerable<[T, U]>; zip<U, R>(other: Iterable<U>, result: (a: T, b: U) => R): Enumerable<R>; append(item: T): Enumerable<T>; prepend(item: T): Enumerable<T>; defaultIfEmpty(value: T): Enumerable<T>; chunk(size: number): Enumerable<T[]>; take(count: number): Enumerable<T>; takeWhile(predicate: Predicate<T>): Enumerable<T>; takeLast(count: number): Enumerable<T>; skip(count: number): Enumerable<T>; skipWhile(predicate: Predicate<T>): Enumerable<T>; skipLast(count: number): Enumerable<T>; first(predicate?: Predicate<T>): T; firstOrDefault(predicate?: Predicate<T>): T | undefined; firstOrDefault<D>(predicate: Predicate<T> | undefined, defaultValue: D): T | D; last(predicate?: Predicate<T>): T; lastOrDefault(predicate?: Predicate<T>): T | undefined; lastOrDefault<D>(predicate: Predicate<T> | undefined, defaultValue: D): T | D; single(predicate?: Predicate<T>): T; singleOrDefault(predicate?: Predicate<T>): T | undefined; singleOrDefault<D>(predicate: Predicate<T> | undefined, defaultValue: D): T | D; elementAt(index: number): T; elementAtOrDefault(index: number): T | undefined; elementAtOrDefault<D>(index: number, defaultValue: D): T | D; any(predicate?: Predicate<T>): boolean; all(predicate: Predicate<T>): boolean; contains(value: T, equals?: EqualityComparer<T>): boolean; sequenceEqual(other: Iterable<T>, equals?: EqualityComparer<T>): boolean; count(predicate?: Predicate<T>): number; sum(this: Enumerable<number>): number; sum(selector: (item: T, index: number) => number): number; average(this: Enumerable<number>): number; average(selector: (item: T, index: number) => number): number; min(): T; min<R>(selector: (item: T, index: number) => R, compare?: KeyComparer<R>): R; max(): T; max<R>(selector: (item: T, index: number) => R, compare?: KeyComparer<R>): R; minBy<K>(key: (item: T) => K, compare?: KeyComparer<K>): T | undefined; maxBy<K>(key: (item: T) => K, compare?: KeyComparer<K>): T | undefined; aggregate(fn: (acc: T, item: T, index: number) => T): T; aggregate<A>(seed: A, fn: (acc: A, item: T, index: number) => A): A; aggregate<A, R>(seed: A, fn: (acc: A, item: T, index: number) => A, result: (acc: A) => R): R; toArray(): T[]; toSet(): Set<T>; toMap<K>(key: (item: T) => K): Map<K, T>; toMap<K, V>(key: (item: T) => K, value: (item: T) => V): Map<K, V>; toDictionary<K>(key: (item: T) => K): Map<K, T>; toDictionary<K, V>(key: (item: T) => K, value: (item: T) => V): Map<K, V>; toLookup<K>(key: (item: T) => K): Lookup<K, T>; toLookup<K, V>(key: (item: T) => K, value: (item: T) => V): Lookup<K, V>; forEach(action: (item: T, index: number) => void): void;}Example:
const big = from([1, 2, 3]).where((n) => n > 1);[...big]; // => [2, 3]big.count(); // => 2 // re-iterableEnumerableGrouping
A group from Enumerable#groupBy: an Enumerable of the members with their key.
class EnumerableGrouping<K, T> extends Enumerable<T> { readonly key: K;}Example:
const users = [{ name: 'Ada', dept: 'HR' }, { name: 'Ben', dept: 'IT' }, { name: 'Cy', dept: 'HR' }];from(users).groupBy((u) => u.dept).select((g) => ({ dept: g.key, n: g.count() })).toArray(); // => [{ dept: 'HR', n: 2 }, { dept: 'IT', n: 1 }]OrderedEnumerable
Result of orderBy/orderByDescending; the only type exposing thenBy.
class OrderedEnumerable<T> extends Enumerable<T> { thenBy<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedEnumerable<T>; thenByDescending<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedEnumerable<T>;}Example:
const rows = [{ dept: 'IT', salary: 50 }, { dept: 'HR', salary: 40 }, { dept: 'IT', salary: 70 }];from(rows).orderBy((r) => r.dept).thenByDescending((r) => r.salary).select((r) => r.salary).toArray(); // => [40, 70, 50]Operators
Section titled “Operators”Functions
Section titled “Functions”aggregate
Folds the sequence. Without a seed the first item seeds the fold (throws HLP_LINQ_NO_ELEMENTS when empty).
function aggregate<T>(fn: (acc: T, item: T, index: number) => T): Terminal<T, T>;function aggregate<T, A>(seed: A, fn: (acc: A, item: T, index: number) => A): Terminal<T, A>;function aggregate<T, A, R>(seed: A, fn: (acc: A, item: T, index: number) => A, result: (acc: A) => R): Terminal<T, R>;Example:
pipe([1, 2, 3], aggregate((a, b) => a + b)); // => 6pipe([1, 2, 3], aggregate('', (s, n) => s + n)); // => '123'pipe([1, 2], aggregate(0, (a, n) => a + n, (a) => a * 10)); // => 30all
True when every item matches (true for empty); stops at the first failure.
function all<T>(predicate: Predicate<T>): Terminal<T, boolean>;Example:
pipe([1, 2], all((n) => n > 0)); // => trueany
True when any item matches (or the sequence is non-empty); stops at the first match.
function any<T>(predicate?: Predicate<T>): Terminal<T, boolean>;Example:
pipe([1, 2], any((n) => n > 1)); // => trueappend
Adds an item at the end.
function append<T>(item: T): Operator<T, T>;Example:
pipe([1], append(2), toArray()); // => [1, 2]assertCount
Throws HLP_LINQ_INVALID_ARGUMENT unless n is a non-negative safe integer.
function assertCount(n: number): void;Example:
assertCount(3); // => undefinedassertCount(-1); // => throws HLP_LINQ_INVALID_ARGUMENTaverage
Arithmetic mean; throws HLP_LINQ_NO_ELEMENTS for empty input.
function average(): Terminal<number, number>;function average<T>(selector: (item: T, index: number) => number): Terminal<T, number>;Example:
pipe([1, 2, 3], average()); // => 2chunk
Splits into arrays of size (the last may be shorter).
function chunk<T>(size: number): Operator<T, T[]>;Example:
pipe([1, 2, 3], chunk(2), toArray()); // => [[1, 2], [3]]concat
Appends another sequence.
function concat<T>(other: Iterable<T>): Operator<T, T>;Example:
pipe([1], concat([2, 3]), toArray()); // => [1, 2, 3]contains
True when the sequence contains value (SameValueZero by default).
function contains<T>(value: T, equals?: EqualityComparer<T>): Terminal<T, boolean>;Example:
pipe([1, NaN], contains(NaN)); // => truecount
Number of matching items (O(1) for arrays, Sets and Maps without a predicate).
function count<T>(predicate?: Predicate<T>): Terminal<T, number>;Example:
pipe([1, 2, 3], count((n) => n > 1)); // => 2defaultCompare
Default key order: nullish/NaN first, then booleans, numbers and bigints, strings (code-unit order), dates.
function defaultCompare(a: unknown, b: unknown): number;Example:
[3, null, 1].sort(defaultCompare); // => [null, 1, 3]defaultIfEmpty
Yields value alone when the source is empty.
function defaultIfEmpty<T>(value: T): Operator<T, T>;Example:
pipe([], defaultIfEmpty(0), toArray()); // => [0]distinct
Removes duplicates (SameValueZero), keeping first occurrences.
function distinct<T>(): Operator<T, T>;Example:
pipe([1, 1, 2], distinct(), toArray()); // => [1, 2]distinctBy
Removes items with a duplicate key, keeping first occurrences.
function distinctBy<T>(key: (item: T) => unknown): Operator<T, T>;Example:
const users = [{ id: 1, email: 'ada@x.io' }, { id: 2, email: 'ben@x.io' }, { id: 3, email: 'ada@x.io' }];pipe(users, distinctBy((u) => u.email), toArray()).map((u) => u.id); // => [1, 2]elementAt
Item at index (negative counts from the end); throws HLP_LINQ_INDEX_OUT_OF_RANGE.
function elementAt<T>(index: number): Terminal<T, T>;Example:
pipe([1, 2, 3], elementAt(-1)); // => 3elementAtOrDefault
Item at index (negative counts from the end), or defaultValue.
function elementAtOrDefault<T>(index: number): Terminal<T, T | undefined>;function elementAtOrDefault<T, D>(index: number, defaultValue: D): Terminal<T, T | D>;Example:
pipe([1], elementAtOrDefault(5)); // => undefinedexcept
Distinct items of the source that do not occur in other.
function except<T>(other: Iterable<T>): Operator<T, T>;Example:
pipe([1, 2, 3, 1], except([2]), toArray()); // => [1, 3]exceptBy
Items whose key does not occur among the keys of other, distinct by key.
function exceptBy<T>(other: Iterable<T>, key: (item: T) => unknown): Operator<T, T>;Example:
const a = [{ id: 1, v: 'a' }, { id: 2, v: 'a' }];const b = [{ id: 2, v: 'b' }, { id: 3, v: 'b' }];pipe(a, exceptBy(b, (x) => x.id), toArray()); // => [{ id: 1, v: 'a' }]first
First matching item; throws HLP_LINQ_NO_ELEMENTS when none.
function first<T>(predicate?: Predicate<T>): Terminal<T, T>;Example:
pipe([1, 2, 3], first((n) => n > 1)); // => 2firstOrDefault
First matching item, or defaultValue (default undefined).
function firstOrDefault<T>(predicate?: Predicate<T>): Terminal<T, T | undefined>;function firstOrDefault<T, D>(predicate: Predicate<T> | undefined, defaultValue: D): Terminal<T, T | D>;Example:
pipe([1], firstOrDefault((n) => n > 5)); // => undefinedpipe([1], firstOrDefault(undefined, 0)); // => 1forEach
Runs action(item, index) for every item.
function forEach<T>(action: (item: T, index: number) => void): Terminal<T, void>;Example:
let total = 0;pipe([1, 2], forEach((n) => { total += n; }));total; // => 3groupBy
Groups by key (SameValueZero) in first-key order; materializes when iterated.
function groupBy<T, K>(key: (item: T) => K): Operator<T, Grouping<K, T>>;function groupBy<T, K, E>(key: (item: T) => K, element: (item: T) => E): Operator<T, Grouping<K, E>>;function groupBy<T, K, E, R>(key: (item: T) => K, element: (item: T) => E, result: (key: K, items: readonly E[]) => R): Operator<T, R>;Example:
pipe(['a', 'bb', 'cc'], groupBy((s) => s.length), select((g) => [g.key, g.items.length]), toArray()); // => [[1, 1], [2, 2]]groupJoin
Pairs each outer item with all matching inner items (possibly none).
function groupJoin<T, U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inners: readonly U[]) => R): Operator<T, R>;Example:
const customers = [{ id: 1, name: 'Acme' }, { id: 2, name: 'Globex' }];const orders = [{ id: 10, customerId: 2 }, { id: 12, customerId: 2 }];pipe(customers, groupJoin(orders, (c) => c.id, (o) => o.customerId, (c, os) => ({ name: c.name, n: os.length })), toArray()); // => [{ name: 'Acme', n: 0 }, { name: 'Globex', n: 2 }]intersect
Distinct items of the source that also occur in other.
function intersect<T>(other: Iterable<T>): Operator<T, T>;Example:
pipe([1, 2, 3, 2], intersect([2, 3]), toArray()); // => [2, 3]intersectBy
Items whose key occurs among the keys of other (the key selector applies to both sequences), distinct by key.
function intersectBy<T>(other: Iterable<T>, key: (item: T) => unknown): Operator<T, T>;Example:
const a = [{ id: 1, v: 'a' }, { id: 2, v: 'a' }];const b = [{ id: 2, v: 'b' }, { id: 3, v: 'b' }];pipe(a, intersectBy(b, (x) => x.id), toArray()); // => [{ id: 2, v: 'a' }]join
Inner equi-join; nullish keys never match. The inner sequence is read once per iteration.
function join<T, U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inner: U) => R): Operator<T, R>;Example:
const customers = [{ id: 1, name: 'Acme' }, { id: 2, name: 'Globex' }];const orders = [{ id: 10, customerId: 2 }, { id: 11, customerId: 3 }];pipe(orders, join(customers, (o) => o.customerId, (c) => c.id, (o, c) => ({ ...o, name: c.name })), toArray()); // => [{ id: 10, customerId: 2, name: 'Globex' }]last
Last matching item; throws HLP_LINQ_NO_ELEMENTS when none.
function last<T>(predicate?: Predicate<T>): Terminal<T, T>;Example:
pipe([1, 2, 3], last()); // => 3lastOrDefault
Last matching item, or defaultValue (default undefined).
function lastOrDefault<T>(predicate?: Predicate<T>): Terminal<T, T | undefined>;function lastOrDefault<T, D>(predicate: Predicate<T> | undefined, defaultValue: D): Terminal<T, T | D>;Example:
pipe([], lastOrDefault()); // => undefinedleftJoin
Left outer join: every outer item appears at least once, with undefined when nothing matches.
function leftJoin<T, U, K, R>(inner: Iterable<U>, outerKey: (item: T) => K, innerKey: (item: U) => K, result: (outer: T, inner: U | undefined) => R): Operator<T, R>;Example:
const depts = [{ id: 'HR', name: 'Human Resources' }];const employees = [{ name: 'Ada', deptId: 'HR' }, { name: 'Ben', deptId: 'IT' }];pipe(employees, leftJoin(depts, (e) => e.deptId, (d) => d.id, (e, d) => [e.name, d?.name]), toArray()); // => [['Ada', 'Human Resources'], ['Ben', undefined]]max
Largest value (nullish and NaN ignored); throws HLP_LINQ_NO_ELEMENTS when none remain.
function max<T>(): Terminal<T, T>;function max<T, R>(selector: (item: T, index: number) => R, compare?: KeyComparer<R>): Terminal<T, R>;Example:
pipe([3, 1, 2], max()); // => 3maxBy
Item with the largest key (first on ties, nullish keys ignored), or undefined.
function maxBy<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): Terminal<T, T | undefined>;Example:
const users = [{ name: 'Ada', age: 36 }, { name: 'Ben', age: 29 }];pipe(users, maxBy((u) => u.age)); // => { name: 'Ada', age: 36 }min
Smallest value (nullish and NaN ignored); throws HLP_LINQ_NO_ELEMENTS when none remain.
function min<T>(): Terminal<T, T>;function min<T, R>(selector: (item: T, index: number) => R, compare?: KeyComparer<R>): Terminal<T, R>;Example:
pipe([3, 1, 2], min()); // => 1const users = [{ name: 'Ada', age: 36 }, { name: 'Ben', age: 29 }];pipe(users, min((u) => u.age)); // => 29minBy
Item with the smallest key (first on ties, nullish keys ignored), or undefined.
function minBy<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): Terminal<T, T | undefined>;Example:
const users = [{ name: 'Ada', age: 36 }, { name: 'Ben', age: 29 }];pipe(users, minBy((u) => u.age)); // => { name: 'Ben', age: 29 }orderBy
Stable ascending sort by key (deferred until iterated).
function orderBy<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): (source: Iterable<T>) => OrderedSequence<T>;Example:
const people = [{ first: 'Ben', last: 'Kay' }, { first: 'Ada', last: 'Lee' }, { first: 'Al', last: 'Kay' }];pipe(people, orderBy((p) => p.last), thenBy((p) => p.first), toArray()).map((p) => p.first); // => ['Al', 'Ben', 'Ada']orderByDescending
Stable descending sort by key (deferred until iterated).
function orderByDescending<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): (source: Iterable<T>) => OrderedSequence<T>;Example:
pipe([1, 3, 2], orderByDescending((n) => n), toArray()); // => [3, 2, 1]pipe
Threads source through operators left to right; the last may be a terminal.
function pipe<A>(source: A): A;function pipe<A, B>(source: A, op1: (a: A) => B): B;function pipe<A, B, C>(source: A, op1: (a: A) => B, op2: (b: B) => C): C;function pipe<A, B, C, D>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D): D;function pipe<A, B, C, D, E>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E): E;function pipe<A, B, C, D, E, F>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E, op5: (e: E) => F): F;function pipe<A, B, C, D, E, F, G>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E, op5: (e: E) => F, op6: (f: F) => G): G;function pipe<A, B, C, D, E, F, G, H>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E, op5: (e: E) => F, op6: (f: F) => G, op7: (g: G) => H): H;function pipe<A, B, C, D, E, F, G, H, I>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E, op5: (e: E) => F, op6: (f: F) => G, op7: (g: G) => H, op8: (h: H) => I): I;function pipe<A, B, C, D, E, F, G, H, I, J>(source: A, op1: (a: A) => B, op2: (b: B) => C, op3: (c: C) => D, op4: (d: D) => E, op5: (e: E) => F, op6: (f: F) => G, op7: (g: G) => H, op8: (h: H) => I, op9: (i: I) => J): J;function pipe(source: unknown, ...ops: ((value: unknown) => unknown)[]): unknown;Example:
pipe([5, 1, 4], where((n) => n > 1), orderBy((n) => n), toArray()); // => [4, 5]prepend
Adds an item at the start.
function prepend<T>(item: T): Operator<T, T>;Example:
pipe([2], prepend(1), toArray()); // => [1, 2]reverse
Reverses the sequence (materializes when iterated).
function reverse<T>(): Operator<T, T>;Example:
pipe([1, 2, 3], reverse(), toArray()); // => [3, 2, 1]select
Maps items with selector(item, index).
function select<T, U>(selector: (item: T, index: number) => U): Operator<T, U>;Example:
pipe([1, 2], select((n, i) => n * 10 + i), toArray()); // => [10, 21]selectMany
Maps each item to a sequence and flattens, optionally projecting (item, child).
function selectMany<T, U>(selector: (item: T, index: number) => Iterable<U>): Operator<T, U>;function selectMany<T, U, R>(selector: (item: T, index: number) => Iterable<U>, resultSelector: (item: T, child: U) => R): Operator<T, R>;Example:
pipe([[1, 2], [3]], selectMany((a) => a), toArray()); // => [1, 2, 3]sequenceEqual
True when both sequences have equal items in the same order.
function sequenceEqual<T>(other: Iterable<T>, equals?: EqualityComparer<T>): Terminal<T, boolean>;Example:
pipe([1, 2], sequenceEqual(new Set([1, 2]))); // => truesingle
The only matching item; throws HLP_LINQ_NO_ELEMENTS or HLP_LINQ_MULTIPLE_ELEMENTS.
function single<T>(predicate?: Predicate<T>): Terminal<T, T>;Example:
pipe([1, 2], single((n) => n > 1)); // => 2singleOrDefault
The only matching item or defaultValue; still throws HLP_LINQ_MULTIPLE_ELEMENTS for several.
function singleOrDefault<T>(predicate?: Predicate<T>): Terminal<T, T | undefined>;function singleOrDefault<T, D>(predicate: Predicate<T> | undefined, defaultValue: D): Terminal<T, T | D>;Example:
pipe([], singleOrDefault()); // => undefinedskip
Skips the first count items.
function skip<T>(count: number): Operator<T, T>;Example:
pipe([1, 2, 3], skip(1), toArray()); // => [2, 3]skipLast
Skips the last count items (buffers count items).
function skipLast<T>(count: number): Operator<T, T>;Example:
pipe([1, 2, 3], skipLast(1), toArray()); // => [1, 2]skipWhile
Skips leading items while predicate(item, index) holds.
function skipWhile<T>(predicate: (item: T, index: number) => boolean): Operator<T, T>;Example:
pipe([1, 2, 5, 1], skipWhile((n) => n < 3), toArray()); // => [5, 1]sum
Compensated sum (0 for empty).
function sum(): Terminal<number, number>;function sum<T>(selector: (item: T, index: number) => number): Terminal<T, number>;Example:
pipe([1, 2, 3], sum()); // => 6const orders = [{ id: 1, total: 120 }, { id: 2, total: 80.5 }];pipe(orders, sum((o) => o.total)); // => 200.5take
Takes the first count items and stops pulling from the source.
function take<T>(count: number): Operator<T, T>;Example:
function* naturals(): Generator<number> { for (let n = 0; ; n++) yield n;}pipe(naturals(), take(3), toArray()); // => [0, 1, 2]takeLast
Takes the last count items (buffers count items).
function takeLast<T>(count: number): Operator<T, T>;Example:
pipe([1, 2, 3], takeLast(2), toArray()); // => [2, 3]takeWhile
Takes leading items while predicate(item, index) holds.
function takeWhile<T>(predicate: (item: T, index: number) => boolean): Operator<T, T>;Example:
pipe([1, 2, 5, 1], takeWhile((n) => n < 3), toArray()); // => [1, 2]thenBy
Ascending tie-breaker; only applies to an orderBy result.
function thenBy<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): (source: OrderedSequence<T>) => OrderedSequence<T>;Example:
const rows = [{ a: 2, b: 1 }, { a: 1, b: 2 }, { a: 1, b: 1 }];pipe(rows, orderBy((r) => r.a), thenBy((r) => r.b), toArray()); // => [{ a: 1, b: 1 }, { a: 1, b: 2 }, { a: 2, b: 1 }]thenByDescending
Descending tie-breaker; only applies to an orderBy result.
function thenByDescending<T, K>(key: (item: T) => K, compare?: KeyComparer<K>): (source: OrderedSequence<T>) => OrderedSequence<T>;Example:
const rows = [{ a: 2, b: 1 }, { a: 1, b: 1 }, { a: 1, b: 2 }];pipe(rows, orderBy((r) => r.a), thenByDescending((r) => r.b), toArray()); // => [{ a: 1, b: 2 }, { a: 1, b: 1 }, { a: 2, b: 1 }]toArray
Collects into an array.
function toArray<T>(): Terminal<T, T[]>;Example:
pipe(new Set([1, 2]), toArray()); // => [1, 2]toLookup
Collects into a Lookup (key -> values), keys in first-occurrence order.
function toLookup<T, K>(key: (item: T) => K): Terminal<T, Lookup<K, T>>;function toLookup<T, K, V>(key: (item: T) => K, value: (item: T) => V): Terminal<T, Lookup<K, V>>;Example:
const users = [{ name: 'Ada', dept: 'HR' }, { name: 'Ben', dept: 'IT' }, { name: 'Cy', dept: 'HR' }];pipe(users, toLookup((u) => u.dept)).get('HR').length; // => 2toMap
Collects into a Map; throws HLP_LINQ_DUPLICATE_KEY on a repeated key.
function toMap<T, K>(key: (item: T) => K): Terminal<T, Map<K, T>>;function toMap<T, K, V>(key: (item: T) => K, value: (item: T) => V): Terminal<T, Map<K, V>>;Example:
const users = [{ id: 1, name: 'Ada' }, { id: 2, name: 'Ben' }];const names = pipe(users, toMap((u) => u.id, (u) => u.name));names.get(2); // => 'Ben'pipe([...users, { id: 1, name: 'Cy' }], toMap((u) => u.id)); // => throws HLP_LINQ_DUPLICATE_KEYtoSet
Collects into a Set.
function toSet<T>(): Terminal<T, Set<T>>;Example:
pipe([1, 1, 2], toSet()); // => new Set([1, 2])union
Distinct items of the source followed by new items of other.
function union<T>(other: Iterable<T>): Operator<T, T>;Example:
pipe([1, 2], union([2, 3]), toArray()); // => [1, 2, 3]unionBy
Union by key; the first item per key wins.
function unionBy<T>(other: Iterable<T>, key: (item: T) => unknown): Operator<T, T>;Example:
const a = [{ id: 1, v: 'a' }, { id: 2, v: 'a' }];const b = [{ id: 2, v: 'b' }, { id: 3, v: 'b' }];pipe(a, unionBy(b, (x) => x.id), toArray()); // => [{ id: 1, v: 'a' }, { id: 2, v: 'a' }, { id: 3, v: 'b' }]where
Keeps items matching predicate(item, index).
function where<T, S extends T>(predicate: (item: T, index: number) => item is S): Operator<T, S>;function where<T>(predicate: (item: T, index: number) => boolean): Operator<T, T>;Example:
pipe([1, 2, 3], where((n) => n > 1), toArray()); // => [2, 3]whereType
Keeps items passing a type guard (the OfType equivalent).
function whereType<T, S extends T>(guard: (item: T) => item is S): Operator<T, S>;Example:
pipe([1, 'a', 2], whereType((x): x is number => typeof x === 'number'), toArray()); // => [1, 2]zip
Pairs items with other position-wise, stopping at the shorter sequence.
function zip<T, U>(other: Iterable<U>): Operator<T, [T, U]>;function zip<T, U, R>(other: Iterable<U>, result: (a: T, b: U) => R): Operator<T, R>;Example:
pipe([1, 2, 3], zip(['a', 'b']), toArray()); // => [[1, 'a'], [2, 'b']]Classes
Section titled “Classes”Grouping
A group of items sharing key. Re-iterable.
class Grouping<K, T> implements Iterable<T> { readonly key: K; readonly items: readonly T[]; [Symbol.iterator](): Iterator<T>;}Example:
const words = ['apple', 'avocado', 'banana'];const groups = pipe(words, groupBy((w) => w.charAt(0)), toArray());groups.map((g) => [g.key, g.items]); // => [['a', ['apple', 'avocado']], ['b', ['banana']]]Lookup
Read-only multi-map from toLookup. Missing keys give an empty array.
class Lookup<K, V> implements Iterable<Grouping<K, V>> { get size(): number; has(key: K): boolean; get(key: K): readonly V[]; [Symbol.iterator](): Iterator<Grouping<K, V>>;}Example:
const users = [{ name: 'Ada', dept: 'HR' }, { name: 'Ben', dept: 'IT' }, { name: 'Cy', dept: 'HR' }];const byDept = pipe(users, toLookup((u) => u.dept));byDept.get('HR').map((u) => u.name); // => ['Ada', 'Cy']byDept.get('Sales'); // => []OrderedSequence
A deferred, stable sort. Iterating materializes and sorts the source (keys computed once per item);
thenBy/thenByDescending add tie-breakers without re-sorting eagerly.
class OrderedSequence<T> implements Iterable<T> { thenBy<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedSequence<T>; thenByDescending<K>(key: (item: T) => K, compare?: KeyComparer<K>): OrderedSequence<T>; [Symbol.iterator](): Iterator<T>;}Example:
const people = [{ name: 'Ravi', age: 41 }, { name: 'Ada', age: 36 }, { name: 'Ben', age: 36 }];[...orderBy((p: { name: string; age: number }) => p.age)(people).thenBy((p) => p.name)].map((p) => p.name); // => ['Ada', 'Ben', 'Ravi']Constants
Section titled “Constants”sameValueZero
SameValueZero, as used by Set, Map, and Array#includes.
const sameValueZero: (a: unknown, b: unknown) => boolean;toDictionary
Alias of toMap (the .NET ToDictionary name).
const toDictionary: typeof toMap;Example:
const users = [{ id: 1, name: 'Ada' }, { id: 2, name: 'Ben' }];pipe(users, toDictionary((u) => u.id)).get(1); // => { id: 1, name: 'Ada' }EqualityComparer
Equality test used by contains and sequenceEqual.
type EqualityComparer<T> = (a: T, b: T) => boolean;KeyComparer
Key comparer: negative, zero, or positive.
type KeyComparer<K> = (a: K, b: K) => number;Operator
Lazy transformation of one sequence into another.
type Operator<T, U> = (source: Iterable<T>) => Iterable<U>;Terminal
Consumes a sequence into a value.
type Terminal<T, R> = (source: Iterable<T>) => R;