@tsdotnet/linq
Version:
A familiar set of functions that operate on JavaScript iterables (ES2015+) in a similar way to .NET's LINQ does with enumerables.
199 lines (141 loc) • 6.41 kB
Markdown
//img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://github.com/tsdotnet/linq/blob/master/LICENSE)


[](https://www.npmjs.com/package/@tsdotnet/linq)
A familiar set of functions that operate on JavaScript iterables (ES2015+) in a similar way to .NET's LINQ does with enumerables.
## Docs
[tsdotnet.github.io/linq](https://tsdotnet.github.io/linq/)
## Source
[GitHub](https://github.com/tsdotnet/linq)
## API
### `linq<T>` vs `linqExtended<T>`
It is possible to do everything with just `linq` but `linqExtended` offers more functionality for those expecting to use common resolutions like `.count`, `.first`, `.last`, etc. Using `linq` will save you some bytes when your common use cases do not need resolutions.
### Iterating
```typescript
for(const e of linq(source).filter(a)) {
// Iterate filtered results.
}
```
```typescript
for(const e of linq(source)
.filterWith(a, b, c)
.transform(x)) {
// Iterate filtered and then transformed results.
}
```
```typescript
for(const e of linq(source)
.where(predicate)
.skip(10).take(10)
.select(mapping)) {
// Iterate filtered and mapped results.
}
```
```typescript
const result = linq(source)
.filterWith(a, b, c)
.transform(x)
.resolve(r);
```
```typescript
const firstElement = linqExtended(source)
.where(predicate)
.select(mapping)
.first();
```
```typescript
import linq from '@tsdotnet/linq/dist/linq';
import range from '@tsdotnet/linq/dist/iterables/range';
import where from '@tsdotnet/linq/dist/filters/where';
import descending from '@tsdotnet/linq/dist/filters/descending';
const source = range(1,100); // Iterable<number>
const filtered = linq(source).filters(
where(n => n%2===1),
descending);
for(const o of filtered) {
// Emit all odd numbers in descending order.
console.log(o); // 99, 97, 95 ...
}
```
```typescript
import linq, {iterables, resolutions} from '@tsdotnet/linq';
const source = iterables.range(1,100); // Iterable<number>
const result = linq(source)
.where(n => n%2===1) // odd numbers only
.resolve(resolutions.sum); // 2500
```
or
```typescript
import linq from '@tsdotnet/linq';
import {range} from '@tsdotnet/linq/dist/iterables';
import {sum} from '@tsdotnet/linq/dist/resolutions';
const source = range(1, 100); // Iterable<number>
const result = linqExtended(source)
.where(n => n%2===1) // odd numbers only
.resolve(sum); // 2500
```
ES2015 enables full support for the [iteration protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols).
Iterables are a significant leap forward in operating with data sequences.
Instead of loading entire sets into arrays or other collections, iterables allow for progressive iteration or synchronous streaming of data.
`tsdotnet/linq` is designed around iterables but also optimized for arrays.
`Iterable<T>` helpers are provided as sources. Calling for an `Iterator<T>` should always start from the beginning and iterators are not shared. Same behavior as LINQ in .NET.
`empty`, `range`, and `repeat` to name a few.
See the [docs](https://tsdotnet.github.io/linq/modules/iterables.html) for a full list.
```typescript
linq(source).filter(a, b);
linq(source).filter(a).filter(b);
linq(source).filter(a).where(predicate);
```
Any function that receives an `Iterable<T>` and returns an `Iterable<T>` is considered an
`IterableFilter<T>`. A filter may result in a different order or ultimately a completely different set than the input but must be of the same type.
There are an extensive set of filters.
See the [docs](https://tsdotnet.github.io/linq/modules/filters.html) for a full list.
```typescript
linq(source).transform(x);
linq(source).filter(a).transform(x);
linq(source).where(predicate).transform(x);
linq(source).where(predicate).select(mapping);
```
Any function that receives an `Iterable<T>` and returns an `Iterable<TResult>` is considered an
`IterableValueTransform<T, TResult>`.
Any filter can be used as a transform, but not every transform can be used as a filter.
`notNull`, `rows`, `select`, `selectMany` and `groupBy` to name a few.
See the [docs](https://tsdotnet.github.io/linq/modules/transforms.html) for a full list.
```typescript
sequence = linq(source);
sequence.resolve(r);
sequence.transform(x).resolve(r);
sequence.filter(a).transform(x).resolve(r);
sequence.where(predicate).resolve(r);
sequence.filterWith(a, b).transform(x).resolve(r);
```
```typescript
sequence = linqExtended(source);
// Examples:
sequence.any(predicate);
sequence.any(); // resolution predicates are optional.
sequence.count(predicate);
sequence.first(predicate);
sequence.last(predicate);
sequence.singleOrDefault(defaultValue, predicate);
sequence.firstOrUndefined(predicate);
sequence.lastOrNull(predicate);
```
A resolution is a transform that takes an `Iterable<T>` and returns `TResult`.
Unlike `.filter(a)` and `.transform(x)`, `.resolve(r)` does not wrap the result in another `Linq<T>`.
There are an extensive set of resolutions.
See the [docs](https://tsdotnet.github.io/linq/modules/resolutions.html) for a full list.
Originally this was a port of `linq.js` converted to full TypeScript under the name `TypeScript.NET Library` and then `TypeScript.NET-Core` with full module support but potentially more than a user might want for a simple task. Instead of .NET style extensions, `Enumerables` incurred a heavy cost of all the extensions under one module.
Modern web standards and practices demanded more granular access to classes and functions. Hence `tsdotnet` was born. `tsdotnet/linq` functionally allows for all the features of its predecessor as well as providing type-safety, and most of the features of LINQ in .NET while not forcing the consumer to download unneeded/undesired modules (extensions).
[![GitHub license](https: