JSPM

  • Created
  • Published
  • Downloads 3355
  • Score
    100M100P100Q119837F
  • License FSFUL

LINQ ported to TypeScript

Package Exports

  • linq-to-typescript

This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (linq-to-typescript) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.

Readme

Linq To TypeScript

Linq to TypeScript

  • Implementation of LINQ for TypeScript
  • Targets TypeScript 2.6 and ES 2016

Getting Started

tsconfig.json

"compilerOptions": {
    "target": "es2016",
    "lib": [
      "dom",
      "es2016",
      "esnext.asynciterable"      
    ],
}

TypeScript

// 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()

API

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

IEnumerable

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>;
}

IAsyncEnumerable

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>;
}

Examples

// 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]

Design

Binding new APIs to Array Types

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

Using Wrappers

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

F.A.Q.

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.