@voltra/streamz v1.0.2

What is Streamz?
Streamz was a project made purely as a playground to look for ideas and ways to implement actual lazy evaluation of collection manipulations. It turns out this is a fairly usable implementation and could actually make a nice library.
Streamz is mainly inspired by C#'s LINQ, Java 8's Stream API, Rust's iter, Kotlin's sequences, C++'s Range-v3 and finally sequency.
How to install Streamz?
First you need to install it via NPM : npm i -S @voltra/streamz
Streamz does not provide a direct "plug into browser" file yet so you'll be required to use a build system (if you are targeting browsers).
Once installed, you can simply require/import it :
import { Stream } from "@voltra/streamz"What does Streamz provide?
Streamz was fully developed using typescript, therefore you have access to the entire source code in the src directory.
The entry point dist/index.js provides the following :
import {
Stream,
Packer,
KeyGen,
ValueGen,
emptyPayloadValue,
isPayloadValueEmpty,
streamIsValidValue,
invalidStreamIteratorPayload
} from "@voltra/streamz"Streamis the class used for collection manipulation, it provides factoriesPackeris used to transform aStreaminto a collection (e.g. an array, an object, aMap, aSet, etc...)KeyGenis an helper object that contains functions destined to be used inPacker#toObjectand similar methodsValueGenis an helper object that contains functions destined to be used inPacker#toObjectand similar methodsemptyPayloadValueis a globalSymboluse to mark invalid payloadsisPayloadValueEmptyis a function that checks whether or not the given value is one of an invalid payloadstreamIsValidValueis a function that checks whether or not the given value is valid (not from an invalid payload)invalidStreamIteratorPayloadis a factory for invalid payloads
src/index.ts provides the following :
import {
Stream,
Packer,
KeyGen,
ValueGen,
emptyPayloadValue,
isPayloadValueEmpty,
streamIsValidValue,
invalidStreamIteratorPayload,
BaseStreamIterator,
EndStreamIterator,
StreamIteratorPayload
} from "@voltra/streamz/src/index"BaseStreamIteratoris an interface that represents both first and intermediate iterators in the iterator chain (i.e. operation chain)EndStreamIteratoris an interface that represents the final iterator in the iterator chain (i.e. operation chain)StreamIteratorPayloadis an interface that represents the payload retrieved when callingnexton an iterator
Helper types are provided under src/types (function.d.ts and helpers.d.ts).
Examples
import { Stream, KeyGen, ValueGen } from "@voltra/streamz"
Stream.of(1, 2, 3, 4) //Stream{1, 2, 3, 4}
.map(x => x+1) //Stream{2, 3, 4, 5}
.filter(x => x%2) //Stream{3, 5}
.pack.toArray(); //[3, 5]
Stream.fromObject({hello: "world", wombo: "combo"})
.map(([k, v]) => ([`key(${k})`, `value(${v})`]))
.pack.toObject(KeyGen.entries, ValueGen.entries);
//Stream{["hello", "world"], ["wombo", "combo"]}
//Stream{["key(hello)", "value(world)"], ["key(wombo)", "value(combo)"]}
//{"key(wombo)": "value(combo)", "key(hello)": "value(world)"}
Stream.of(1, 2, 1, 1, 2, 1) //Stream{1, 2, 1, 1, 2, 1}
.map(x => x+1) //Stream{2, 3, 2, 2, 3, 2}
.unique() //Stream{2, 3}
.pack.toArray(); //[2, 3]
Stream.of(2, 4, 6, 8) //Stream{2, 4, 6, 8}
.map(x => x+1) //Stream{3, 5, 7, 9}
.all(x => x%2); //trueimport { Stream } from "@voltra/streamz"
const X = Stream.of(1, 2, 3, 4)
.map(x => x+1)
.filter(x => x%2)
.pack.toArray();
console.log(X);
//Is roughly equivalent to
const X = [];
for(const it in [1, 2, 3, 4]){
const x = it+1;
if(x % 2)
X.push(x);
}
console.log(X);Changes
A complete JSDoc will be provided once ready.
v1.0.2
extend in src/extend.ts (also exported in src/index.ts and therefore in dist/index.js) :
- Installs global prototype extensions :
Array#stream()isStream.from(this)Object#stream()isStream.fromObject(this)Set#stream()isStream.fromSet(this)Map#stream()isStream.fromMap(this)
- And class methods extensions
Object.fromStreamsisStream.zipToObjectNumber.rangeisStream.rangeNumber.infiniteRangeisStream.infinite
Compare in src/utils.ts (also exported in src/index.ts and therefore in dist/index.js) :
Compare.asc(lhs, rhs)standard comparison function (ascending)Compare.desc(lhs, rhs)standard comparison function (descending)Compare.mapped"namespace" for mapped comparison functionsCompare.mapped.asc(mapper)crafts aCompare#ascwhere elements were mapped usingmapperCompare.mapped.desc(mapper)crafts aCompare#descwhere elements were mapped usingmapper
Stream initial operations :
Stream.fromMap(map)creates a stream from aMapStream.fromSet(set)creates a stream from aSet
Stream intermediate operations :
- Regular
Stream#chunked(size = 3)groups items in chunks whose size is at mostsize
- Zipping
Stream#zip(stream)combines this stream (makes a pair using one item from each stream)Stream.zip(lhs, rhs)same aslhs.zip(rhs)Stream#zipBy(stream, mapper)combines this stream using the mapper function to craft the new itemStream.zipBy(lhs, rhs, mapper)same aslhs.zipBy(rhs, mapper)
- Filtering
Stream#nonNull()filters out anynullStream#nonFalsy()filters out any falsy value (0,null,undefined, etc...)Stream#filterNot(predicate)filters element that satisfy the predicateStream#filterOut(predicate)alias forStream#filterNot(predicate)Stream#filterIsInstance(class)filters out elements that are not instance ofclass
- Index manipulation
Stream#takeWhile(predicate)keeps item untilpredicateis not satisfiedStream#takeUntil(predicate)keeps item whilepredicateis not satisfied
Stream terminal operations :
- Regular
Stream#count(predicate = (_ => true))counts the elements that satisfy the predicate
- Predicate tests
Stream#contains(elem)checks whether or notelemis an item of this stream
- Index based
Stream#atIndex(index)retrieves the element at the given index (or null if there is none)Stream#atIndexOr(index, default)retrieves the element or get backdefaultStream#first()retrieves the first element (ornull)Stream#firstOr(default)retrieves the first element (ordefault)
- Zipping
Stream#zipToObject(stream)zips into an object usingthisas keys andstreamas valuesStream.zipToObject(keys, values)equivalent tokeys.zipToObject(values)Stream#unzip()unzips a stream of pairs into a pair of arraysStream#unzipBy(firstGen, lastGen)unzips a stream into a pair of arrays applyingfirstGenfor the first element of the pair andlastGenfor the last element of the pairStream#unzipVia(mapper)convenience method to unzip a stream of pairs into a pair of arrays using a single mapper function that returns a pair
- Sorting
Stream#sortedWith(comparator)packs to an array sorted usingcomparatoras the comparison functionStream#sortedBy(mapper)maps the objects and sort in ascending order via regular comparison operatorsStream#sortedDescBy(mapper)same assortedBybut in descending orderStream#sorted()same assortedBybut without mappingStream#sortedDesc()same assortedbut in descending order
v1.0.1 and inferior
Factories (creators)
Streamz is not a usual object, it doesn't really expect you to call its constructor, it provides a lot of static factory methods in order for you to get the most fluent experience possible :
Stream.of(...args)takes any amount of parameters and makes a stream out of itStream.from(args)takes an array and makes a stream out of itStream.fromObject(obj)takes an object and makes a stream out of itStream.range(higher)takes a number used as its lower bound makes the range [0 ; higher) as a streamStream.range(lower, higher, step = 1)takes both bounds, the step and makes the range [lower ; higher) as a stream (usesstepas increment instead of 1)Stream.infinite(lower = 0)makes a stream for the range [lower ; Infinity)
Intermediate computations (intermediary)
Streamz offers a lot of intermediate computations, these are not effective until a blocking computation is requested :
Stream#map(mapper)maps each element using the provided mapper functionStream#filter(predicate)only gives back elements that satisfy the provided predicateStream#peek(functor)applies the function on each element and passes itStream#unique()removes duplicates (uses aSetbehind the scenes to check for duplicates)Stream#take(amount = 10)only keeps the firstx ≤ amountelementsStream#skip(amount = 10)skips the firstx ≤ amountelementsStream#between(begin = 0, end = 10, excludeRight = false)takes the element whose index is in the range begin ; end ifexcludeRight == falseotherwise from [begin ; end)
Blocking computations (terminators)
Operations
Common
Stream#forEach(functor)applies the provided function on each elementStream#reduce(reducer, acc)computes the reduced value by getting a newaccby applying the reducer function and the currentaccand the current element
Predicative
Stream#all(predicate)checks whether or not every element satisfies the given predicateStream#any(predicate)checks whether or not any element satisfies the given predicateStream#none(predicate)checks whether or not none of the elements satisfies the given predicate
Packing
Stream#pack is an instance of Packer and exposes the following :
Packer#toArray()converts the stream into an arrayPacker#toObjectBy(keyGen)converts the stream into an object whose keys are generated usingkeyGenon the current element and whose values are the elements of the streamPacker#toObject(keyGen, valueGen)converts the stream into an object whose keys and values are generated using the current element onkeyGenandvalueGenPacker#toSet()converts the stream into aSetPacker#toMapBy(keyGen)converts the stream into aMapwhose keys are generated usingkeyGenon the current element and whose values are the elements of the streamPacker#toMap(keyGen, valueGen)converts the stream into aMapwhose keys and values are generated using the current element onkeyGenandvalueGen