Linq to TypeScript
- Implementation of LINQ for TypeScript
- Targets TypeScript 2.6 and ES 2016
"compilerOptions": {
"target": "es2016",
"lib": [
"dom",
"es2016",
"esnext.asynciterable"
],
}
// 0. Import Module
import { ArrayEnumerable, Enumerable, AsyncEnumerable, initializeLinq } from "linq-to-typescript"
// To Use With Wrappers
const evenNumbers = Enumerable.from([1, 2, 3, 4, 5, 6, 7, 8, 9]).where((x) => x % 2 === 0).toArray()
// To Use Without Wrappers
// 1. Declare that the JS types implement the IEnumerable interface
declare global {
interface Array<T> extends IEnumerable<T> {
concat(items: IEnumerable<T>): IEnumerable<T>;
concat(...items: Array<ReadonlyArray<T>>): ArrayEnumerable<T>;
concat(...items: Array<T | ReadonlyArray<T>>): ArrayEnumerable<T>;
}
interface Uint8Array extends IEnumerable<number> { }
interface Uint8ClampedArray extends IEnumerable<number> { }
interface Uint16Array extends IEnumerable<number> { }
interface Uint32Array extends IEnumerable<number> { }
interface Int8Array extends IEnumerable<number> { }
interface Int16Array extends IEnumerable<number> { }
interface Int32Array extends IEnumerable<number> { }
interface Float32Array extends IEnumerable<number> { }
interface Float64Array extends IEnumerable<number> { }
interface Map<K, V> extends IEnumerable<[K, V]> { }
interface Set<T> extends IEnumerable<T> { }
}
// 2. Bind Linq Functions to Array and Map
initializeLinq()
// 3. Use without a wrapper type
const evenNumbers = [1, 2, 3, 4, 5, 6, 7, 8, 9].where((x) => x % 2 === 0).toArray()
LinqToTypeScript implements the functionality of the IEnumerable interface
- IEnumerable and IAsyncEnumerable interfaces based on,
- IEnumerable<T> Interface
- Some changes made due to conflics with existing method names
- Some changes made due to limitations of JavaScript
interface IEnumerable<TSource> extends Iterable<TSource> {
aggregate(func: (x: TSource, y: TSource) => TSource): TSource;
aggregate<TAccumulate>(seed: TAccumulate, func: (x: TAccumulate, y: TSource) => TAccumulate): TAccumulate;
aggregate<TAccumulate, TResult>(seed: TAccumulate, func: (x: TAccumulate, y: TSource) => TAccumulate, resultSelector: (x: TAccumulate) => TResult): TResult;
all(predicate: (x: TSource) => boolean): boolean;
any(predicate?: (x: TSource) => boolean): boolean;
average(this: IEnumerable<number>): number;
average(selector: (x: TSource) => number): number;
concat(second: IEnumerable<TSource>): IEnumerable<TSource>;
contains(value: TSource, comparer?: IEqualityComparer<TSource>): boolean;
count(predicate?: (x: TSource) => boolean): number;
distinct(comparer?: IEqualityComparer<TSource>): IEnumerable<TSource>;
elementAt(index: number): TSource;
elementAtOrDefault(index: number): TSource | null;
except(second: IEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IEnumerable<TSource>;
first(predicate?: (x: TSource) => boolean): TSource;
firstOrDefault(predicate?: (x: TSource) => boolean): TSource | null;
each(action: (x: TSource) => void): IEnumerable<TSource>;
groupBy(keySelector: (x: TSource) => number): IEnumerable<IGrouping<number, TSource>>;
groupBy(keySelector: (x: TSource) => string): IEnumerable<IGrouping<string, TSource>>;
groupBy<TKey>(keySelector: (x: TSource) => TKey, comparer: IEqualityComparer<TKey>): IEnumerable<IGrouping<TKey, TSource>>;
groupByWithSel<TElement>(keySelector: ((x: TSource) => number), elementSelector: (x: TSource) => TElement): IEnumerable<IGrouping<number, TElement>>;
groupByWithSel<TElement>(keySelector: ((x: TSource) => string), elementSelector: (x: TSource) => TElement): IEnumerable<IGrouping<string, TElement>>;
groupByWithSel<TKey, TElement>(keySelector: ((x: TSource) => TKey), elementSelector: (x: TSource) => TElement, comparer: IEqualityComparer<TKey>): IEnumerable<IGrouping<TKey, TElement>>;
intersect(second: IEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IEnumerable<TSource>;
joinByKey<TInner, TKey, TResult>(inner: IEnumerable<TInner>, outerKeySelector: (x: TSource) => TKey, innerKeySelector: (x: TInner) => TKey, resultSelector: (x: TSource, y: TInner) => TResult, comparer?: IEqualityComparer<TKey>): IEnumerable<TResult>;
last(predicate?: (x: TSource) => boolean): TSource;
lastOrDefault(predicate?: (x: TSource) => boolean): TSource | null;
max(this: IEnumerable<number>): number | never;
max(selector: (x: TSource) => number): number | never;
min(this: IEnumerable<number>): number | never;
min(selector: (x: TSource) => number): number | never;
ofType(type: "object"): IEnumerable<Object>;
ofType(type: "function"): IEnumerable<Function>;
ofType(type: "symbol"): IEnumerable<Symbol>;
ofType(type: "boolean"): IEnumerable<boolean>;
ofType(type: "number"): IEnumerable<number>;
ofType(type: "string"): IEnumerable<string>;
ofType<TResult>(type: IConstructor<TResult>): IEnumerable<TResult>;
orderBy(predicate: (x: TSource) => number | string): IOrderedEnumerable<TSource>;
orderBy(predicate: (x: TSource) => number, comparer: IComparer<number>): IOrderedEnumerable<TSource>;
orderBy(predicate: (x: TSource) => string, comparer: IComparer<string>): IOrderedEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => number | string): IOrderedEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => number, comparer: IComparer<number>): IOrderedEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => string, comparer: IComparer<string>): IOrderedEnumerable<TSource>;
reverse(): IEnumerable<TSource>;
select<OUT>(selector: (x: TSource) => OUT): IEnumerable<OUT>;
select<TKey extends keyof TSource>(key: TKey): IEnumerable<TSource[TKey]>;
selectMany<OUT>(selector: (x: TSource) => Iterable<OUT>): IEnumerable<OUT>;
selectMany<TBindedSource extends {
[key: string]: Iterable<TOut>;
}, TOut>(this: IEnumerable<TBindedSource>, selector: keyof TBindedSource): IEnumerable<TOut>;
sequenceEquals(second: IEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): boolean;
single(predicate?: (x: TSource) => boolean): TSource;
singleOrDefault(predicate?: (x: TSource) => boolean): TSource | null;
skip(count: number): IEnumerable<TSource>;
skipWhile(predicate: (x: TSource, index: number) => boolean): IEnumerable<TSource>;
sum(this: IEnumerable<number>): number;
sum(this: IEnumerable<TSource>, selector: (x: TSource) => number): number;
take(amount: number): IEnumerable<TSource>;
takeWhile(predicate: (x: TSource, index: number) => boolean): IEnumerable<TSource>;
toArray(): TSource[];
toMap<TKey>(selector: (x: TSource) => TKey): Map<TKey, TSource[]>;
toSet(): Set<TSource>;
union(second: IEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IEnumerable<TSource>;
where(predicate: (x: TSource, index: number) => boolean): IEnumerable<TSource>;
zip<TSecond>(second: Iterable<TSecond>): IEnumerable<ITuple<TSource, TSecond>>;
zip<TSecond, TResult>(second: Iterable<TSecond>, resultSelector: (x: TSource, y: TSecond) => TResult): IEnumerable<TResult>;
[Symbol.iterator]: () => IterableIterator<TSource>;
}
interface IAsyncEnumerable<TSource> extends AsyncIterable<TSource> {
aggregate(func: (x: TSource, y: TSource) => TSource): Promise<TSource>;
aggregate<TAccumulate>(seed: TAccumulate, func: (x: TAccumulate, y: TSource) => TAccumulate): Promise<TAccumulate>;
aggregate<TAccumulate, TResult>(seed: TAccumulate, func: (x: TAccumulate, y: TSource) => TAccumulate, resultSelector: (x: TAccumulate) => TResult): Promise<TResult>;
all(predicate: (x: TSource) => boolean): Promise<boolean>;
any(predicate?: (x: TSource) => boolean): Promise<boolean>;
average(this: IAsyncEnumerable<number>): Promise<number>;
average(selector: (x: TSource) => number): Promise<number>;
concat(second: IAsyncEnumerable<TSource>): IAsyncEnumerable<TSource>;
contains(value: TSource, comparer?: IEqualityComparer<TSource>): Promise<boolean>;
count(predicate?: (x: TSource) => boolean): Promise<number>;
distinct(comparer?: IEqualityComparer<TSource>): IAsyncEnumerable<TSource>;
each(action: (x: TSource) => void): IAsyncEnumerable<TSource>;
elementAt(index: number): Promise<TSource>;
elementAtOrDefault(index: number): Promise<TSource | null>;
except(second: IAsyncEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IAsyncEnumerable<TSource>;
first(predicate?: (x: TSource) => boolean): Promise<TSource>;
firstOrDefault(predicate?: (x: TSource) => boolean): Promise<TSource | null>;
groupBy(keySelector: (x: TSource) => number): IAsyncEnumerable<IGrouping<number, TSource>>;
groupBy(keySelector: (x: TSource) => string): IAsyncEnumerable<IGrouping<string, TSource>>;
groupBy<TKey>(keySelector: (x: TSource) => TKey, comparer: IEqualityComparer<TKey>): IAsyncEnumerable<IGrouping<TKey, TSource>>;
groupByWithSel<TElement>(keySelector: ((x: TSource) => number), elementSelector: (x: TSource) => TElement): IAsyncEnumerable<IGrouping<number, TElement>>;
groupByWithSel<TElement>(keySelector: ((x: TSource) => string), elementSelector: (x: TSource) => TElement): IAsyncEnumerable<IGrouping<string, TElement>>;
groupByWithSel<TKey, TElement>(keySelector: ((x: TSource) => TKey), elementSelector: (x: TSource) => TElement, comparer: IEqualityComparer<TKey>): IAsyncEnumerable<IGrouping<TKey, TElement>>;
intersect(second: IAsyncEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IAsyncEnumerable<TSource>;
joinByKey<TInner, TKey, TResult>(inner: IAsyncEnumerable<TInner>, outerKeySelector: (x: TSource) => TKey, innerKeySelector: (x: TInner) => TKey, resultSelector: (x: TSource, y: TInner) => TResult, comparer?: IEqualityComparer<TKey>): IAsyncEnumerable<TResult>;
last(predicate?: (x: TSource) => boolean): Promise<TSource>;
lastOrDefault(predicate?: (x: TSource) => boolean): Promise<TSource | null>;
max(this: IAsyncEnumerable<number>): Promise<number | never>;
max(selector: (x: TSource) => number): Promise<number | never>;
min(this: IAsyncEnumerable<number>): Promise<number | never>;
min(selector: (x: TSource) => number): Promise<number | never>;
ofType(type: "object"): IAsyncEnumerable<Object>;
ofType(type: "function"): IAsyncEnumerable<Function>;
ofType(type: "symbol"): IAsyncEnumerable<Symbol>;
ofType(type: "boolean"): IAsyncEnumerable<boolean>;
ofType(type: "number"): IAsyncEnumerable<number>;
ofType(type: "string"): IAsyncEnumerable<string>;
ofType<TResult>(type: IConstructor<TResult>): IAsyncEnumerable<TResult>;
orderBy(predicate: (x: TSource) => number | string): IOrderedAsyncEnumerable<TSource>;
orderBy(predicate: (x: TSource) => number, comparer: IComparer<number>): IOrderedAsyncEnumerable<TSource>;
orderBy(predicate: (x: TSource) => string, comparer: IComparer<string>): IOrderedAsyncEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => number | string): IOrderedAsyncEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => number, comparer: IComparer<number>): IOrderedAsyncEnumerable<TSource>;
orderByDescending(predicate: (x: TSource) => string, comparer: IComparer<string>): IOrderedAsyncEnumerable<TSource>;
reverse(): IAsyncEnumerable<TSource>;
select<OUT>(selector: (x: TSource) => OUT): IAsyncEnumerable<OUT>;
select<TKey extends keyof TSource>(key: TKey): IAsyncEnumerable<TSource[TKey]>;
selectMany<OUT>(selector: (x: TSource) => Iterable<OUT>): IAsyncEnumerable<OUT>;
selectMany<TBindedSource extends {
[key: string]: Iterable<TOut>;
}, TOut>(this: IAsyncEnumerable<TBindedSource>, selector: keyof TBindedSource): IAsyncEnumerable<TOut>;
sequenceEquals(second: IAsyncEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): Promise<boolean>;
single(predicate?: (x: TSource) => boolean): Promise<TSource>;
singleOrDefault(predicate?: (x: TSource) => boolean): Promise<TSource | null>;
skip(count: number): IAsyncEnumerable<TSource>;
skipWhile(predicate: (x: TSource, index: number) => boolean): IAsyncEnumerable<TSource>;
sum(this: IAsyncEnumerable<number>): Promise<number>;
sum(this: IAsyncEnumerable<TSource>, selector: (x: TSource) => number): Promise<number>;
take(amount: number): IAsyncEnumerable<TSource>;
takeWhile(pedicate: (x: TSource, index: number) => boolean): IAsyncEnumerable<TSource>;
toArray(): Promise<TSource[]>;
toMap<TKey>(selector: (x: TSource) => TKey): Promise<Map<TKey, TSource[]>>;
toSet(): Promise<Set<TSource>>;
union(second: IAsyncEnumerable<TSource>, comparer?: IEqualityComparer<TSource>): IAsyncEnumerable<TSource>;
where(predicate: (x: TSource, index: number) => boolean): IAsyncEnumerable<TSource>;
zip<TSecond, TResult>(second: IAsyncEnumerable<TSecond>, resultSelector: (x: TSource, y: TSecond) => TResult): IAsyncEnumerable<TResult>;
zip<TSecond>(second: IAsyncEnumerable<TSecond>): IAsyncEnumerable<ITuple<TSource, TSecond>>;
[Symbol.asyncIterator]: () => AsyncIterableIterator<TSource>;
}
// AGGREGATE
[1, 2].aggregate((x, y) => x + y) // 3
["f", "o", "o"].aggregate((x, y) => x + y) // "foo"
[1, 2, 3].aggregate(4, (acc, x) => acc + x) // 10
[1, 2, 3].aggregate("seed", (acc, y) => acc + y, acc => acc + "result") // "seed123result"
// ALL
[1, 2].all(x => x < 3) // true
[1, 2].all(x => x < 2) // false
// ANY
[0].any() // true
[true].any(x => !x) // false
// CONCAT
[1, 2].concat([2, 3]) // [1, 2, 2, 3]
// CONTAINS
[1, 2, 3].contains(1) // true
[1, "2", "3"].contains(2, EqualityComparer) // true
// COUNT
[1, 2, 3].count() // 3
[true, true, false].count(x => x) // false
// DISTINCT
["f", "o", "o"].distinct() // "foo"
["1", 1, 2, 2, 3, "3"].distinct(EqualityComparer) // ["1", 2, 3]
// ENUMERATEOBJECT
for (let item of Enumerable.enumerateObject(object)) {
// ...
}
// EACH
let y = 0;
[1, 2].each(x => y += x)
// ELEMENTAT
[1, 2].elementAt(1) // 2
// ELEMENTATORDEFAULT
[1, 2].elementAtOrDefault(3) // null
// EXCEPT
[1, 2].except([1]) // [2]
([1, 2] as Linq.IEnumerable<string | number>).except(["1"], EqualityComparer) // [2]
// FIRST
[1, 2].first() // 1
[1, 2].first(x => x === 2) // 2
// FIRSTORDEFAULT
[].firstOrDefault() // null
// FLATTEN
Linq.Enumerable.flatten([1, [2, 3]]) // [1, 2, 3]
// GROUPBY
const groupByBreed = cats.groupBy(cat => cat.breed)
const groupByBreedThenAge = groupByBreed.thenBy(cat => cat.age)
// INTERSECT
[1, 2, 3].intersect([1, 2]) // [1, 2]
[1, 2, "3"].intersect(["1", "2"], EqualityComparer) // [1, 2]
// PLUCK
// TAKE
[1, 2, 3, 4, 5].take(2) // [1, 2]
// TAKEWHILE
// LAST
[1, 2].last() // 2
[1, 2].last(x => x === 1) // 1
// LASTORDEFAULT
[].lastOrDefault() // null
[1, 2, 3].lastOrDefault(x => x === 4) // null
// MAX
[1, 2, 3].max() // 3
[1, 2, 3].max(x => x * x) // 9
// MIN
[1, 2, 3, -7].min() // -7
[1, 2, 3, -7].min(Math.abs) // 1
// OFTYPE
["str", "str2", 1, 2, 3, {}].ofType("string") // ["str", "str2"]
[1, 2, "4", false, true].ofType("boolean") // [false, true]
// ORDERBY
[3, 4, 7, 0, 1].orderBy(x => x) // [0, 1, 3, 4, 7]
// REVERSE
[1, 2, 3].reverse() // [3, 2, 1]
// SELECT
[1, 2, 3].select(x => x * 10) // [10, 20, 30]
// SELECTMANY
[1, 2, 3].selectMany((x) => [x, x * x]) // [1, 1, 2, 4, 3, 9]
[1, 2, 3].skip(2) // [3]
// UNION
[1, 2, 3].union([4, 5, 6]) // [1, 2, 3, 4, 5, 6]
JavaScript doesn't have extension methods like in C#, therefore we extend the class itself with new methods.
Call initializeLinq
to bind library functions to default Array methods,
The following collections support IEnumerable
,
- Array
- Map
- Set
- String
- Int8Array
- Int16Array
- Int32Array
- Uint8Array
- Uint8ClampedArray
- Uint16Array
- Uint32Array
- Float32Array
- Float64Array
Wrappers are safer as they won't interfere with other libraries.
// To Create an IAsyncEnumerable<T>
AsyncEnumerable.from(asyncIterableIteratorOrPromiseArray)
// To Create an IEnumerable<T>
Enumerable.from(iterableIteratorOrArray)
The root class is Enumerable
. Collections simple call Enumerable
methods with the first argument being this
Q
I am getting a Cannot find module 'core-js/modules/es7.symbol.async-iterator'
A
Ensure you have core-js
NPM package installed.
Q Why did you create this?
A For fun and to gain understanding of TypeScript and Node Package Manager.
Q Why not target ES5?
A Backward compatibility is not a concern. You are free to recompile this targeting ES5 instead.
Q How does this compare to other LINQ libraries?
A They are more mature.