README
rillet.js
Lazily evaluated ranges/streams/fp library built around ES6 generators
The Javascript Array class provide a number of methods that we typically associate with functional languages - filter/concat/forEach/etc. However, they're generally eagerly and exhaustively evaluated.
Rillet.js uses ES6 iterators to build these same methods in a lazily evaluated, streamable style, along the lines of .NET LINQ methods, Java 8 Streams, that kind of thing.
Installation
Rillet.js is available from npm
npm install rillet
Trivial Example
const from = require('rillet').from;
const array_or_other_iterable = fn();
from(array_or_other_iterable).
filter(n => n > 100).
forEach(x => console.log(x));
More Involved Example
const rillet = require('./rillet.js');
const { of, zip, numbers } = rillet;
const fizzes = () => of('', '', 'Fizz').cycle();
const buzzes = () => of('', '', '', '', 'Buzz').cycle();
const words = () => zip(fizzes(), buzzes()).map(([f,b]) => `${f}${b}`)
const integers = () => numbers(1);
const fizzbuzz = (howMany) =>
zip(words(), integers()).
map(([w, n]) => !!w ? w : n).
take(howMany);
fizzbuzz(100).forEach(console.log);
Methods
Creation methods
Range.from(iterable)
creates a Range from an array-like or other iterable object (similar toArray.from()
)Range.of(...items)
creates a Range with a variable number of arguments (similar toArray.of
)Range.zip(...iterables)
create a Range which returns a series of arrays, each containing the corresponding item from each iterables. E.g. givenp, q, ...
return [p[0], q[0]], [p[1], q[1]], ... The sequence terminates when any of the iterables is exhausted.Range.numbers(start = 0, step = 1)
creates a range of numbers,start
,start + step
,start + 2*step
...Range.empty()
create an empty Range - a new Range with no items. Directly equivalent tofrom([])
orof()
The returned range is an iterable object, and so can be used in all the places where Javascript accepts an iterable - eg for/of loop, etc
Modifiers
Each modifier method returns a new Range which can be iterated on, or modified with further chained operations.
Range.prototype.filter(predicate)
filters the returned sequence so only those elements that pass the test thatpredicate
implements.Range.prototype.map(fn)
returns the result of applyingfn
to each item in the sequenceRange.prototype.flatMap(fn)
appliesfn
to each item in the sequence, then flattens the result into a new sequenceRange.prototype.take(count)
returns only the firstcount
items in the sequenceRange.prototype.takeWhile(predicate)
returns items from the start of the sequence for as long as the predicate is trueRange.prototype.drop(count)
discards the firstcount
items of the sequence, and returns the remainderRange.prototype.dropWhile(predicate)
discards items from the start sequence for as long as the predicate is true, returning the remainderRange.prototype.concat(...items)
creates a lazily concatenated iterable who's elements are all the elements of the sequence followed by all the elements of itemsRange.prototype.flat([depth])
flattens any iterables in the sequence, down to thedepth
given. Thedepth
is optional and if omitted or is less that 1, it defaults to 1.Range.prototype.flatten()
flattens any iterables in the sequence, creating a new stream which is entirely flatRange.prototype.uniq([fn])
filters duplicates from the stream. The projectionfn
is optional, defaulting to the identity function.Range.prototype.compact()
removes all falsey values from the stream -0, false, null, undefined, '', NaN
are all falsey.Range.prototype.cycle()
returns an infinite sequence, looping over the items in the stream.
Terminal methods
A terminal method drains the range producing a single result.
Range.prototype.reduce(bifn, [initial])
appliesfn
against an accumulator and each element in the sequence, to reduce to a single value. The optionalinitial
is used as the first argument to the first call offn
. If noinitial
is given, the first element in the sequence is used. Calling reduce on an empty sequence without an initial value will throw
Strictly speaking, reduce is all you need. However, for readability and expressiveness, rillet.js provides a number of methods implementing common terminal operations
Range.prototype.forEach(fn)
appliesfn
to each item in the sequence. Returns undefinedRange.prototype.count()
returns the count of items in the sequenceRange.prototype.first()
returns the first value in the sequence, or throws if the sequence is emptyRange.prototype.firstOrDefault(defaultValue)
returns the first value in the sequence, or the defaultValue if the sequence is emptyRange.prototype.last()
returns the last value in the sequence, or throws if the sequence is empty. Will never return if the sequence is unbounded.Range.prototype.lastOrDefault(defaultValue)
returns the last value in the sequence, or the defaultValue if the sequence is empty.Range.prototype.max([comparator])
finds the maximum item of the stream, according to the provided comparator. If no comparator is provided, it defaults to(item, currentmax) => item > currentmax
. Returns undefined if the sequence is empty.Range.prototype.min([comparator])
finds the minimum item of the stream, according to the provided comparator. If no comparator is provided, it defaults to(item, currentmax) => item < currentmax
. Returns undefined if the sequence is empty.Range.prototype.sum()
calculates the arithmetic sum of the items in the sequence, calling Number(x) on each item. The sum of the empty range is 0.Range.prototype.none(predicate)
returns true if none of the items in the sequence match the predicate, and false otherwiseRange.prototype.every(predicate)
returns true if all of the items in the sequence match the predicate, and false otherwiseRange.prototype.some(predicate)
returns true if one or more of the items in the sequence match the predicate, and false otherwiseRange.prototype.join(separator)
join all the items in the sequence into a string, using the provided separator. If the separator is not given, the items are separated with a comma.Range.prototype.toArray()
gathers all the items in the sequence into an array.
In the notes above,
fn
has the formx => operation(x)
, where operation returns a valuebifn
has the form(x,y) => operation(x, y)
, where operation returns a valuepredicate
has the formx => test(x)
, where test returns a booleancomparator
has the form(x,y) => compare(x, y)
, where compare returns true if the comparison of x and y succeeds. E.g. a comparate functionless_then(x, y)
would return true if x was less that y, and false otherwise.- where a method has the same name and form as a method on Array, it will exhibit the same behaviour
Name
A rillet is small rill. What's a rill? A very small brook. A brook? It's a small stream.
Every other stream related name was already taken on npm and this library is only a very small library, hence rillet.
License
Copyright (c) 2017 JezUK Ltd
Licensed under the terms of the MIT License.