Skip to content

sergeyrassokhin/lazystream.ts

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

16 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LazyStream.TS

Inspired by Java 8 Streams and .NET LINQ, this small library provides an easy way to work with collections in FP-like and SQL-like styles.

Class: Stream <T>

Stream class that wraps Iterable<T> and provides most of the common FP-style operations like map, filter, flatMap etc. as well as SQL-style select, where, join etc. Stream does also implement Iterable<T> so it can wrap other stream, and since most of the operations return Stream<T>, the calls can be easily chained with each other, for example:

const numbers: Array<number> = ...
const evenSquares = new Stream(numbers)
   .filter(v => v % 2 == 0)
   .map(v => v * v)
   .toArray();

Types can be primitives (as in the example above) but can also be Objects, so SQL-like instructions can be used:

const users: Array<{name: string, age: number, city: string}> = ...
const usersByCity = new Stream(users)
  .where("age", ">=", 25)
  .groupBy("city")
  .toArray();

Type parameters

T

Type of elements in the stream. Can be any type including Object, primitives, Iterable etc.

Summary of operations

FP-style

  • filter
  • map
  • reduce
  • flat
  • flatMap

SQL-style

  • groupBy
  • join
  • select
  • where

Collectors

  • forEach
  • toArray
  • toMap

Shortcuts

  • last
  • max
  • min

Constructors

constructor

+ new Stream(iterableOrIteratorFactory: Iterable‹T› | function): Stream

Defined in index.ts:149

Creates a new instance of Stream by wrapping existing Iterable<T> or just a function that returns Iterator<T> (so generator function can be passed as well). For example:

// from Iterable<T>
const fromIterable = new Stream([1,2,3,4]);

// from () => Iterator<T>
function* generator() {
  yield 1;
  yield 2;
  yield 3;
}
const fromGenerator = new Stream(generator);

Parameters:

Name Type Description
iterableOrIteratorFactory Iterable‹T› | function instance of Iterable<T> or () => Iterator<T>

Returns: Stream

Methods

[Symbol.iterator]

[Symbol.iterator](): Iterator‹T›

Defined in index.ts:179

Returns: Iterator‹T›


filter

filter(predicate: function): Stream‹T›

Defined in index.ts:210

Filters elements given a predicate function. Example of usage:

const evens = new Stream([1, 2, 3])
  .filter(i => i % 2 == 0)
  .toArray();

expect(even).toStrictEqual([2]);

Parameters:

predicate: function

function that accepts element and return true or false

▸ (t: T): boolean

Parameters:

Name Type
t T

Returns: Stream‹T›


flat

flat(this: Stream‹Iterable‹any››): Stream‹T extends Iterable ? E : never›

Defined in index.ts:238

Flattens elements of this Stream<Iterable<T>> into Stream<T>. Example of usage:

const flattened = new Stream([[1,3], [2,4]])
   .flat()
   .toArray();

expect(flattened).toStrictEqual([1, 3, 2, 4]);

Parameters:

Name Type
this Stream‹Iterable‹any››

Returns: Stream‹T extends Iterable ? E : never›


flatMap

flatMap<K>(field: K): Stream‹Val<T, K> extends Iterable ? E : never›

Defined in index.ts:259

First maps elements of Stream<T> to Stream<Iterable<R>> and then flattens into Stream<R>. For example:

const flattened = new Stream([{numbers: [1, 3]}, {numbers: [2, 4]}])
  .flatMap("numbers") 
  .toArray();
// "numbers" - name of a field that contains Iterable

expect(flattened).toStrictEqual([1, 3, 2, 4]);

Type parameters:

K: FieldOrFn‹T›

Parameters:

Name Type Description
field K field extractor see FieldOrFn<T>

Returns: Stream‹Val<T, K> extends Iterable ? E : never›


forEach

forEach(fn: Fn‹T›): void

Defined in index.ts:562

Fetches all elements from the stream and executes given function for each element. Example of usage:

var sum = 0;
new Stream([1, 2, 3])
  .forEach(v => sum += v);

expect(sum).toStrictEqual(6);

Parameters:

Name Type Description
fn Fn‹T› function to invoke

Returns: void


groupBy

groupBy<K>(keyFieldOrFn?: K): Stream‹object›

Defined in index.ts:516

Mimics SQL GROUP BY operator. Groups elements with the same given key. Keys must be in a sorted order, otherwise will raise an exception. Next element is emitted only once the element with the next key appears in the stream. Example of usage:

const ungrouped = [{a: 1, b: "val 1"}, {a: 1, b: "val 2"}, 
                   {a: 2, b: "val 3"}, {a: 2, b: "val 4"}];
const grouped = new Stream(ungrouped)
  .groupBy("a")
  .toArray();

expect(grouped).toStrictEqual([
  {key: 1, values: [{a: 1, b: "val 1"}, {a: 1, b: "val 2"}]},
  {key: 2, values: [{a: 2, b: "val 3"}, {a: 2, b: "val 4"}]}
]);

Type parameters:

K: FieldOrFn‹T›

Parameters:

Name Type Description
keyFieldOrFn? K

Returns: Stream‹object›


join

join<O, KC, KL, KR, JT>(other: Iterable‹O›, params: object): Stream‹JoinEntry‹T, O, JT››

Defined in index.ts:369

Mimics SQL JOIN operator. Depending on type of join, for each element of source stream lookups a corresponding element from joined stream, using join parameter that can either be a field, or a function.

If using SORTED join type, it will ensure that keys from both set come in the specified order, otherwise will raise an exception. This is the most intended usage of this operation as it won't need to pre-fetch all elements from second stream in memory. The output keys order is preserved, i.e. if join order is ascending, then join entries will be emitted with the keys being in ascending order.

If using UNSORTED join type, it will pre-fetch all elements from second stream into a hash map before any join entries can be emitted.

Keys are not required to be unique, for duplicate keys, join entries will be multiplied, like it happens in SQL:

[1,2,2] left join [2,2,3] = [{1,null}, {2,2}, {2,2}, {2,2}, {2,2}]

Example of usage:

let collectionA = [{a: 1, b: "val 1",}, {a: 2, b: "val 2",}, {a: 3, b: "val 3",}];
let collectionB = [{a: 1, b: "val a",}, {a: 2, b: "val b",}];
const joined = new Stream(collectionA)
  .join(collectionB, {joinType: JoinType.INNER_ASC, commonKey: "a"})
  .toArray();

expect(result).toStrictEqual([
  {key: 1, left: {a: 1, b: "val 1"}, right: {a: 1, b: "val a"}},
  {key: 2, left: {a: 2, b: "val 2"}, right: {a: 2, b: "val b"}}
]);

Type parameters:

O - type of elements in the other stream being joined

Parameters:

other: Iterable‹O› - other stream to join (right-side)

params: Optional object= {joinType?, commonKey?, leftKey?, rightKey?}

Name Type Description
commonKey? FieldOrFn‹T | O› Common field that belongs to both streams elements, or a function that can extract key from T | O; if provided, next 2 will be ignored.
leftKey? FieldOrFn‹O› Field that belongs to current (left) stream elements, or a function that can extract key from T. If not provided, then elements will be used as keys and are supposed to be primitive.
rightKey? JoinType Field that belongs to other (right) stream elements, or a function that can extract key from O. If not provided, then elements will be used as keys and are supposed to be primitive.
joinType? FieldOrFn‹T› Join mode, default value is JoinType.INNER_ASC.

JoinType is defining a semantic of a join operation as follows:

JoinType Left Right Result
INNER_ASC [1,2] [2,3] [{key: 2, left: 2, right: 2}]
LEFT_ASC [1,2] [2,3] [{key: 1, left: 2, right: null}, {key: 2, left: 2, right: 2}]
RIGHT_ASC [1,2] [2,3] [{key: 2, left: 2, right: 2}, {key: 3, left: null, right: 3}]
FULL_ASC [1,2] [2,3] [{key: 1, left: 1, right: null}, {key: 2, left: 2, right: 2}, {key: 3, left: null, right: 3}]
INNER_DESC [2,1] [3,2] [{key: 2, left: 2, right: 2}]
LEFT_DESC [2,1] [3,2] [{key: 2, left: 2, right: 2}, {key: 1, left: 2, right: null}]
RIGHT_DESC [2,1] [3,2] [{key: 3, left: 3, right: null}, {key: 2, left: 2, right: 2}]
FULL_DESC [2,1] [3,2] [{key: 3, left: 3, right: null}, {key: 2, left: 2, right: 2}, {key: 1, left: 2, right: null}]
INNER_UNSORTED [1,3,2] [2,1,4] [{key: 1, left: 2, right: 1}, {key: 2, left: 2, right: 2}]
LEFT_UNSORTED [1,3,2] [2,1,4] [{key: 1, left: 2, right: 1}, {key: 3, left: null, right: 3}, {key: 2, left: 2, right: 2}]

Returns: Stream‹JoinEntry‹T, O, JT››


last

last(): T

Defined in index.ts:638

Fetches all elements from the stream and returns the last element. Example of usage:

const result = new Stream([1, 2, 3]).last();
expect(result).toStrictEqual(3);

Returns: T


map

map<R>(mapFn: function): Stream‹R›

Defined in index.ts:225

Maps each element of a current stream with a mapper function and returns new Stream of newly created elements. Example of usage:

const squares = new Stream([1, 2, 3])
  .map(i => i * i)
  .toArray();

Type parameters:

R

Parameters:

mapFn: function

function that accepts current element and returns a new one. The function can optionally accept prev element (original and mapped one).

▸ (t: T, prevT?: T, prevR?: R): R

Parameters:

Name Type
t T
prevT? T
prevR? R

Returns: Stream‹R›


max

max(this: Stream‹number›): number

Defined in index.ts:664

Fetches all elements from the stream and returns the maximal one. Example of usage:

const result = new Stream([1, 2, 3]).max();
expect(result).toStrictEqual(3);

Currently only supports numeric streams, proper comparator support to be added soon.

Parameters:

Name Type
this Stream‹number›

Returns: number


min

min(this: Stream‹number›): number

Defined in index.ts:651

Fetches all elements from the stream and returns the minimal one. Example of usage:

const result = new Stream([1, 2, 3]).min();
expect(result).toStrictEqual(1);

Currently only supports numeric streams, proper comparator support be added soon.

Parameters:

Name Type
this Stream‹number›

Returns: number


reduce

reduce<R>(reduceFn: function): R

Defined in index.ts:618

Fetches all elements from the stream and recursively applies reduceFn to each element providing previous result to each call. Example of usage:

const result = new Stream([1, 2, 3])
 .reduce<number>((a, b) => (a === undefined ? 0 : a) + b);

expect(result).toStrictEqual(6);

Type parameters:

R

Parameters:

reduceFn: function

function(accumulator: R | undefined, current: T, index: number) => R

▸ (accumulator: R | undefined, current: T, index: number): R

Parameters:

Name Type
accumulator R | undefined
current T
index number

Returns: R


select

select<U1, U2>(field1: U1, ...field2: U2[]): Stream‹[U2] extends [never] ? T[U1] : object›

Defined in index.ts:283

Mimics SQL SELECT operator, by restricting set of fields that need be returned. Accepts list of field names, and returns objects with only those fields, for example:

const selectedAB = stream([{a: 1, b: "str", c: 3}]).select("a", "b");
const selectedA = stream([{a: 1, b: "str", c: 3}]).select("a");

expect(selectedAB).toStrictEqual([{a: 1, b: "str"}]);
expect(selectedA).toStrictEqual([1]);

Type parameters:

U1: Field‹T›

U2: Field‹T›

Parameters:

Name Type Description
field1 U1 field name
...field2 U2[] field name (none or many)

Returns: Stream‹[U2] extends [never] ? T[U1] : object›


toArray

toArray(): Array‹T›

Defined in index.ts:571

Fetches all elements from the stream and collect them int Array<T>.

Returns: Array‹T›


toMap

toMap<K, V>(key: K, val?: V): Map‹Val‹T, K›, Val‹T, V››

Defined in index.ts:594

Fetches all elements from the stream and collect them int Map<K, V>. Example of usage:

const result = new Stream([1, 2, 3])
  .toMap(t => `${t}`);

expect(result).toStrictEqual(new Map([['1', 1], ['2', 2], ['3', 3]]));

Type parameters:

K: FieldOrFn‹T›

V: FieldOrFn‹T›

Parameters:

Name Type Description
key K key field or function
val? V value field or function

Returns: Map‹Val‹T, K›, Val‹T, V››


where

where<K>(field: K, compareOp: ComparisonOp, value: Val‹T, K›): Stream‹T›

Defined in index.ts:314

Mimics SQL WHERE clause, by including only the records that satisfy given parameters. Example of usages:

const objs = [{a: 1, b: "val a", c: 3}, {a: 2, b: "val b", c: 4}, 
              {a: 3, b: "val c", c: 4}];
const result = new Stream(objs)
  .where("a", "<", 3)
  .where("b", "!=", "val a")
  .toArray();

expect(result).toStrictEqual([{a: 2, b: "val b", c: 4}]);

Type parameters:

K: FieldOrFn‹T›

Parameters:

Name Type Description
field K field name, see Field<T>
compareOp ComparisonOp comparison operation, see ComparisonOp
value Val‹T, K› value of type T[Field] to compare with

Returns: Stream‹T›

Creating streams from existing collections

from

from<T>(first: Iterable‹T›, second: Iterable‹T›, ...rest: Iterable‹T›[]): Stream‹T[]›

Defined in index.ts:21

Cross-multiplies multiple Iterable's of the same type T into Stream<T[]>. Can accept any number of args. This function can be used as a shortcut - from(someIterable) instead of new Stream(someIterable).

Example of usage:

const result = from([1, 3], [2, 4])
  .toArray();

expect(result).toStrictEqual([[1, 3], [1, 4], [2, 3], [2, 4]]);

Type parameters:

T

Parameters:

Name Type Description
first Iterable‹T› (required) first Iterable
second Iterable‹T› (optional) second Iterable
...rest Iterable‹T›[] (optional) other Iterables

Returns: Stream‹T[]›


range

range(from: number, to: number): Stream‹number›

Defined in index.ts:60

Creates a Stream<number> with all numbers from the range. Example of usage:

const result = range(0, 10)
  .toArray();

expect(result).toStrictEqual([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);

Parameters:

Name Type Description
from number start of range (inclusive)
to number end of range (inclusive)

Returns: Stream‹number›


About

LazyStream.TS provides an easy way to work with collections in FP-like and SQL-like styles.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published