typescript-extended-linq
This is a library that is a direct translation of System.Linq from .NET and many additional functions from MoreLINQ.
Table of Contents
Installation:
npm i typescript-extended-linq
You can optionally bind the Linq functions to native types (arrays, maps, sets, strings, etc). Binding to native types adds the functions to the type's prototype. Always be mindful when modifying a native type's prototype. While these functions will not affect native functionality, it cannot be guaranteed that it will not affect other frameworks if they also modify prototypes.
Add the following at the start of your program to bind to native types (the eslint disable is only needed if you use that eslint rule) Note, if adding the declarations to its own d.ts file, be sure to import IEnumerable and the Extension interfaces for IDEs to work right:
/* eslint-disable @typescript-eslint/no-empty-interface */
import { bindLinqToNativeTypes } from 'typescript-extended-linq';
declare global {
interface Array<T> extends Omit<IEnumerable<T>, 'forEach' | 'toString' | 'toJSON' | symbol>, IArrayExtensions<T> {}
interface Int8Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Int16Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Int32Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Uint8ClampedArray extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Uint16Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Uint32Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Float32Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Float64Array extends Omit<IEnumerable<number>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Map<K, V> extends Omit<IEnumerable<[K, V]>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface Set<T> extends Omit<IEnumerable<T>, 'forEach' | 'toString' | 'toJSON' | symbol> {}
interface String
extends Omit<IEnumerable<string>, 'endsWith' | 'startsWith' | 'split' | 'toString' | 'toJSON' | symbol> {}
}
bindLinqToNativeTypes();
You will then be able to see Linq methods on native types without needing to use the from()
wrapper:
[1, 2, 3]
.where(x => x > 1)
.take(1)
.toArray();
Any function that already existed on the native type (toString()
, forEach()
for example) will not be overridden and will continue to use the native function.
Note that the call to bindLinqToNativeTypes
must be done prior to using the functions on native types. This, for example, will not work:
foo.ts
export const foo = [1, 2, 3].where(x => x > 1).toArray();
index.ts
import { bindLinqToNativeTypes } from 'typescript-extended-linq';
import { foo } from './foo';
bindLinqToNativeTypes();
console.log(foo);
This will not work because the import line is before the bind function is ran and when that file is imported, it tries to use the 'where' function.
This, however, will work fine:
foo.ts
export function foo(): number[] {
return [1, 2, 3].where(x => x > 1).toArray();
}
index.ts
import { bindLinqToNativeTypes } from 'typescript-extended-linq';
import { foo } from './foo';
bindLinqToNativeTypes();
console.log(foo());
Because the 'where' is not used until the foo function is invoked which is after the bind function is called.
If you only want to bind to certain types, you can pass in an optional object to do so (be sure to update the global type declarations):
import { bindLinqToNativeTypes } from 'typescript-extended-linq';
bindLinqToNativeTypes({ types: [Array] }); // Now only array will have new functions. Sets, Strings, Maps, etc will not.
You can also pass in optional functions to not bind to native types (once again, be sure to update the global type declarations):
import { bindLinqToNativeTypes } from 'typescript-extended-linq';
bindLinqToNativeTypes({ types: [Array], functionsToIgnore: ['aggregate'] }); // Only arrays will have new functions but it will not have aggregate.
Why use this library?
Additional Functionality
- Native JavaScript/TypeScript provides many useful functions that are similar to LINQ (such as map, filter, reduce, etc). This library fills in the missing gaps with many functions that are not included in the native language such as joins, multi-level ordering, grouping, etc.
Additional Collections
- This library comes with List, LinkedList, Stack, Queue, and PriorityQueue collections all which have all the linq functions.
Deferred Execution
- Just like LINQ, this library uses deferred execution and lazy evaluation.
TypeScript First
- This library was written in Typescript so type definitions are included out of the box and are always up to date. The Typescript source code is included in the package so users can easily look at the implementation.
- Documentation is also auto-generated on every release so they will always be up to date as well.
Basic Usage:
import { from } from 'typescript-extended-linq';
const items = [
{ id: 1, foo: 'a', bar: new Date('8/1/2021') },
{ id: 2, foo: 'a', bar: new Date('8/1/2021') },
{ id: 2, foo: 'b', bar: new Date('8/1/2021') },
{ id: 2, foo: 'a', bar: new Date('9/1/2021') },
{ id: 3, foo: 'a', bar: new Date('8/1/2021') },
{ id: 3, foo: 'b', bar: new Date('8/1/2021') }
];
const query = from(items)
.where(item => item.id % 2 === 0)
.orderBy(item => item.id)
.thenBy(item => item.foo)
.thenBy(item => item.bar);
/**
* Will log:
* [
* { id: 2, foo: 'a', bar: 2021-08-01T07:00:00.000Z },
* { id: 2, foo: 'a', bar: 2021-09-01T07:00:00.000Z },
* { id: 2, foo: 'b', bar: 2021-08-01T07:00:00.000Z }
* ]
*/
console.log(query.toArray());
const sumOfIds = query.sum(item => item.id);
// Will log 6
console.log(sumOfIds);
const distinct = query.distinctBy(item => item.id).toArray();
// Will log [ { id: 2, foo: 'a', bar: 2021-08-01T07:00:00.000Z } ]
console.log(distinct);
Each function also can be used directly by passing in the source iterable as the first argument:
import { join } from 'typescript-extended-linq';
type Person = { name: string };
type Pet = { name: string; owner: Person };
const magnus: Person = { name: 'Magnus' };
const terry: Person = { name: 'Terry' };
const adam: Person = { name: 'Adam' };
const john: Person = { name: 'John' };
const barley: Pet = { name: 'Barley', owner: terry };
const boots: Pet = { name: 'Boots', owner: terry };
const whiskers: Pet = { name: 'Whiskers', owner: adam };
const daisy: Pet = { name: 'Daisy', owner: magnus };
const scratchy: Pet = { name: 'Scratchy', owner: { name: 'Bob' } };
const people = from([magnus, terry, adam, john]);
const pets = from([barley, boots, whiskers, daisy, scratchy]);
const result = join(
people,
pets,
person => person,
pet => pet.owner,
(person, pet) => ({ ownerName: person.name, pet: pet.name })
).toArray();
/**
* Will log:
* [
* { ownerName: 'Magnus', pet: 'Daisy' },
* { ownerName: 'Terry', pet: 'Barley' },
* { ownerName: 'Terry', pet: 'Boots' },
* { ownerName: 'Adam', pet: 'Whiskers' }
* ]
*/
console.log(result);
Documentation
Please see full documentation here.
aggregate
Applies an accumulator function over a sequence.
all
Determines whether all elements of a sequence satisfy a condition.
any
Determines whether any element of a sequence exists or satisfies a condition.
append
Appends a value to the end of the sequence.
asEnumerable
Returns the input as an IEnumerable.
assert
Tests a sequence with a given predicate. An error will be thrown if any element fails the sequence.
atLeast
Determines whether or not the number of elements in the sequence is greater than or equal to the given integer.
atMost
Determines whether or not the number of elements in the sequence is lesser than or equal to the given integer.
average
Computes the average of a sequence of numeric values.
chunk
Split the elements of a sequence into chunks of size at most chunkSize.
concatenate
Concatenates two sequences.
contains
Determines whether a sequence contains a specified element.
count
Returns the number of elements in a sequence.
defaultIfEmpty
Returns the elements of the specified sequence or the specified value in a singleton collection if the sequence is empty.
distinct
Returns distinct elements from a sequence.
distinctBy
Returns distinct elements from a sequence according to a specified key selector function.
elementAt
Returns the element at a specified index in a sequence. A negative index can be used to get element starting from the end.
elementAtOrDefault
Returns the element at a specified index in a sequence or null if the index is out of range. A negative index can be used to get element starting from the end.
endsWith
Determines whether the end of the first sequence is equivalent to the second sequence.
except
Produces the set difference of two sequences.
exceptBy
Produces the set difference of two sequences according to a specified key selector function.
first
Returns the first element in a sequence. Throws if sequence contains no elements.
firstOrDefault
Returns the first element in a sequence. Returns null if sequence contains no elements
flatten
Returns a new IEnumerable with all sub-iterable elements concatenated into it recursively up to the specified depth.
forEach
Iterates the sequence and calls an action on each element.
fullJoinHeterogeneous
Performs a full outer join on two heterogeneous sequences.
fullJoinHomogeneous
Performs a full outer join on two homogeneous sequences.
groupBy
Groups the elements of a sequence according to a specified key selector function.
groupJoin
Correlates the elements of two sequences based on key equality, and groups the results.
innerJoin
Performs an inner join by correlating the elements of two sequences based on matching keys.
intersect
Produces the set intersection of two sequences.
intersectBy
Produces the set intersection of two sequences according to a specified key selector function.
interweave
Interweaves multiple sequences.
last
Returns the last element of a sequence.
lastOrDefault
Returns the last element of a sequence, or null if the sequence contains no elements.
leftJoinHeterogeneous
Performs a left outer join on two heterogeneous sequences. Additional arguments specify key selection functions and result projection functions.
leftJoinHomogeneous
Performs a left outer join on two homogeneous sequences. Additional arguments specify key selection functions and result projection functions.
max
Returns the maximum value in a sequence of values.
maxBy
Returns the maximum value in a generic sequence according to a specified key selector function.
min
Returns the min value in a sequence of values.
minBy
Returns the min value in a generic sequence according to a specified key selector function.
ofType
Filters the elements of an IEnumerable based on a specified type.
order
Sorts the elements of a sequence in ascending order.
orderDescending
Sorts the elements of a sequence in descending order.
orderBy
Sorts the elements of a sequence in ascending order.
orderByDescending
Sorts the elements of a sequence in descending order.
pipe
Executes the given action on each element in the source sequence and yields it.
prepend
Adds a value to the beginning of the sequence.
quantile
Computes the quantile of a sequence.
reverseImmutable
Inverts the order of the elements in a sequence.
rightJoinHeterogeneous
Performs a right outer join on two heterogeneous sequences.
rightJoinHomogeneous
Performs a right outer join on two homogeneous sequences.
select
Projects each element of a sequence into a new form.
selectMany
Projects each element of a sequence to an IEnumerable and flattens the resulting sequences into one sequence.
sequenceEqual
Determines whether two sequences are equal by comparing the elements.
shuffle
Returns a new IEnumerable of the input sequence in random order.
single
Returns the only element of a sequence that satisfies a specified condition, and throws an exception if more than one such element exists.
singleOrDefault
Returns a single, specific element of a sequence, or null if that element is not found.
skip
Bypasses a specified number of elements in a sequence and then returns the remaining elements.
skipLast
Returns a new enumerable collection that contains the elements from source with the last count elements of the source collection omitted.
skipWhile
Bypasses elements in a sequence as long as a specified condition is true and then returns the remaining elements.
split
Splits the source sequence by a separator.
startsWith
Determines whether the beginning of the first sequence is equivalent to the second sequence.
sum
Computes the sum of a sequence of numeric values.
take
Returns a specified number of contiguous elements from the start of a sequence.
takeEvery
Returns every N-th element of a sequence.
takeLast
Returns a new enumerable collection that contains the last count elements from source.
takeWhile
Returns elements from a sequence as long as a specified condition is true, and then skips the remaining elements.
thenBy
Performs a subsequent ordering of the elements in a sequence in ascending order.
thenByDescending
Performs a subsequent ordering of the elements in a sequence in descending order.
to
Creates a new instance of the passed in ctor with the Iterable as input.
toArray
Converts the source sequence into an array.
toMap
Creates a Map<TKey, TValue> from an IEnumerable according to specified key selector.
toObject
Returns an object with keys selected by keySelector and values of TSource.
toSet
Creates a Set from an IEnumerable.
union
Produces the set union of two sequences.
unionBy
Produces the set union of two sequences according to a specified key selector function.
where
Filters a sequence of values based on a predicate.
xor
Produces the symmetric difference of two sequences.
xorBy
Produces the symmetric difference of two sequences according to a specified key selector function.
zip
Produces a sequence of tuples with elements from the two specified sequences.