The API documentation is split into to main sections: types and methods.
Many APIs share types. These named types are used in the formal type definitions, but also throughout the documentation for consistency and clarify.
If you aren't already familiar with the technical definition of an iterable and an iterator, I strongly recommend you first read the MDN docs on iterators and generators and iteration protocols.
An object implementing the iterable protocol, which is to say possessing a Symbol.iterator
method.
null
, undefined
, or Iterable.
An Iterable which is also an iterator, which is to say that it has next()
, throw(error)
, and return(value)
methods. It can be evaluated multiple times calling its [Symbol.iterator]()
method repeatedly. Note that there is no guarantee that evaluating a result iterable more than once will produce the same values. The result will be repeatable only if any transformations are repeatable and the source iterable returns the same results on each iteration.
A single iterator with a Symbol.iterator
implementation of return this
. Once exhausted it will only ever produce empty results, which is a common source of errors.
An IterableIterator of SingletonIterableIterators which represents the result of some method-specic algorithm for choosing split points in a source
. Parts are thus non-overlapping subsequences of values from source
, and each part is (under the hood). Parts are essentially decoration on a single iterator over source
, so advancing to the next part will cause any attempt to take values from previous parts to throw an error. Working around this limitation is simple when needed: just store each part in an array with map(toArray, partsIterable)
.
An object implementing the async iterable protocol, which is to say possessing a Symbol.asyncIterator
method
null
, undefined
, AsyncIterable, or Iterable.
The async version of a IterableIterator. Instead of working as an Iterable and an iterator, it behaves like an AsyncIterable and an async iterator. The same caveats apply regarding evaluating this kind of result iterable more than once.
A single async iterator with a Symbol.asyncIterator
implementation of return this
. Once exhausted it will only ever produce empty results.
The async version of a PartsIterable, which is to say an AsyncIterableIterator of SingletonAsyncIterableIterators. As with PartsIterable
, only one part can be active at a time.
A compare
callback is used to determine sort order. These methods have the same API the callback used in Array.prototype.sort
. compare
callbacks must always return synchronously, even when sorting async iterables.
A default value is always provided, again the same as is used by Array.prototype.sort
:
(a, b) => (a > b ? 1 : b > a ? -1 : 0);
This code sorts numbers by their value, and strings lexicographically.
Create iterables
cycle (async)
cycleTimes (async)
range
repeat
repeatTimes
Create iterables from objects
objectEntries
objectKeys
objectValues
Use iterables from data structures
wrapEntries
wrapKeys
wrapValues
Transform a single iterable
append (async)
drop (async)
dropWhile (async)
enumerate (async)
filter (async)
flat (async)
flatMap (async)
interpose (async)
interposeSeq (async)
map (async)
prepend (async)
reverse (async)
slice (async)
take (async)
takeSorted (async)
takeWhile (async)
tap (async)
window (async)
windowAhead (async)
windowBehind (async)
wrap (async)
Separate an iterable into multiple iterables
batch (async)
bisect (async)
split (async)
splitGroups (async)
splitOn (async)
splitOnAny (async)
splitOnAnySeq (async)
splitOnSeq (async)
splitWhen (async)
Combine multiple iterables
collate (async)
compress (async)
concat (async)
asyncInterleaveReady
join (async)
joinWith (async)
joinWithSeq (async)
roundRobin (async)
zip (async)
zipAll (async)
Reduce an iterable to a single value
deepEqual (async)
equal (async)
every (async)
find (async)
findOr (async)
first (async)
firstOr (async)
includes (async)
includesAny (async)
includesAnySeq (async)
includesSeq (async)
isEmpty (async)
isObject
isSorted (async)
notObject
reduce (async)
size (async)
some (async)
startsWith (async)
startsWithAny (async)
startsWithAnySeq (async)
startsWithSeq (async)
str (async)
takeLast (async)
takeLastOr (async)
Control timing inside an async iterable
Cache an iterable
Consume an iterable
arrayFrom
arrayFromAsync
consume (async)
forEach (async)
objectFrom
objectFromAsync
stringFrom
stringFromAsync
toArray (async)
toObject (async)
Predicates (test a value)
isAsyncIterable
isAsyncLoopable
isAsyncWrappable
isIterable
isLoopable
isNil
isNull
isUndefined
isWrappable
notAsyncIterable
notAsyncLoopable
notAsyncWrappable
notIterable
notLoopable
notNil
notNull
notUndefined
notWrappable
Utilities
apply
arrayFirst
arrayFirstOr
arrayLast
arrayLastOr
arrayReverse
call
compose
execPipe
getSize
pipe
when
Generator helpers
forkerate (async)
interleave (async)
peekerate (async)
spliterate (async)
spliterateGrouped (async)
Yields the contents of iterable
repeated for the longest time (forever).
cycle(range(1, 4)); // Iterable[1, 2, 3, 1, 2, 3, 1, 2, 3, ...]
asyncCycle(source)
__asyncCycle(source)
See cycle
cycleTimes(n, source)
__cycleTimes(source, n)
Yields the contents of iterable
repeated n
times.
cycleTimes(2, range(1, 4)); // Iterable[1, 2, 3, 1, 2, 3]
asyncCycleTimes(n, source)
__asyncCycleTimes(source, n)
See cycleTimes
range(start, end, ?step)
range(?end)
range({ start, end, step })
__range(?start, ?end, ?step)
Defaults:
start
: 0end
: Infinitystep
: 1
Create an iterable returning a sequence of numbers (the sequence can be infinite).
range(); // 0, 1, 2 ... Infinity
range(3); // 0, 1, 2
range(3, 6); // 3, 4, 5
range(3, 10, 3); // 3, 6, 9
range(9, 3, -3 }); // 9, 6
range({ end: 3 }); // 0, 1, 2
range({ start: 3 }); // 3, 4, 5 ... Infinity
range({ start: 3, end: 6 }); // 3, 4, 5
range({ start: 3, end: 10, step: 3 }); // 3, 6, 9
repeat(value)
__repeat(value)
Create an iterable that yields the same value
ad infintum.
repeat('x'); // Iterable['x', 'x', 'x', ... 'x', .........]
repeatTimes(n, value)
__repeatTimes(n, value)
Create an iterable that yields the same value
n
times.
repeatTimes(4, 'x'); // Iterable['x', 'x', 'x', 'x']
objectEntries(obj)
__objectEntries(obj)
Yields the [key, value]
entries of own, iterable (in the object sense) properties of obj
. When obj
is null
or undefined
it yields nothing, but it is otherwise equivalent to Object.keys
.
objectEntries
is a great way to construct Map
instances from objects!
objectEntries({ foo: 'bar', fox: 'far' }); // Iterable[['foo': 'bar'], ['fox': 'far']]
new Map(objectEntries(obj)); // Map{foo => 'bar', fox => 'far'}
objectKeys(obj)
__objectKeys(obj)
Yields the string names of the own, iterable (in the object sense) properties of obj
. When obj
is null
or undefined
it yields nothing, but it is otherwise equivalent to Object.keys
.
objectKeys({ foo: 'bar', fox: 'far' }); // Iterable['foo', 'fox'];
objectValues(obj)
__objectValues(obj)
Yields the values of own, iterable (in the object sense) properies of obj
. When obj
is null
or undefined
it yields nothing, but it is otherwise equivalent to Object.values
.
objectValues({ foo: 'bar', fox: 'far' }); // Iterable['bar', 'far']
wrapEntries(entriesable)
__wrapEntries(entriesable)
Yields the values yielded by entriesable.entries()
. When passed null
or undefined
, yields nothing.
wrapEntries(new Map([
['foo', 'bar'],
['fox', 'far']
])); // Iterable[['foo', 'bar'], ['fox', 'far']]
wrapKeys(keysable)
__wrapKeys(keysable)
Yields the values yielded by keysable.keys()
. When passed null
or undefined
, yields nothing.
wrapKeys(new Map([
['foo', 'bar'],
['fox', 'far']
])); // Iterable['foo', 'fox']
wrapValues(valuesable)
__wrapValues(valuesable)
Yields the values yielded by valuesable.values()
. When passed null
or undefined
, yields nothing.
wrapValues(new Map([
['foo', 'bar'],
['fox', 'far']
])); // Iterable['bar', 'far']
append(value, source)
__append(source, value)
Yields values from source
with value
appended.
append(4, [1, 2, 3]); // Iterable[1, 2, 3, 4]
asyncAppend(value, source)
__asyncAppend(source, value)
See append
drop(n, iterable)
__drop(iterable, n)
Yields values from source
, omitting the first n
values.
drop(1, ['a', 'b', 'c']); // Iterable['b', 'c']
asyncDrop(n, iterable)
__asyncDrop(iterable, n)
See drop
dropWhile(predicate, source)
__dropWhile(source, predicate)
Returns values from source
, omitting consecutive values at the beginning of source
for which the result of predicate(value, idx)
is truthy.
dropWhile(isEven, range(5)); // 0, 2, 4
asyncDropWhile(predicate, source)
__asyncDropWhile(source, predicate)
See dropWhile
enumerate(start, source)
enumerate(source)
__enumerate(source, ?start)
It is a shorthand for zipping an index to an iterable:
enumerate(repeat('x')); // Iterable[[0, 'x'], [1, 'x'], [2, 'x'], ...]
You can also specify a startIdx which will be the index of the first value.
enumerate(1, 'abc'); // Iterable[[1, 'a'], [2, 'b'], [3, 'c']]
asyncEnumerate(start, source)
asyncEnumerate(source)
__asyncEnumerate(source, ?start)
See enumerate
filter(predicate, source)
__filter(source, predicate)
Yields only values from source
for which the result of predicate(value, idx)
is truthy. Equivalent to Array.prototype.filter
.
filter(isEven, range(4)); // Iterable[0, 2]
filter((animal) => animal.kind.slice(1) === 'at', [
{ type: 'cat' },
{ type: 'rat' },
{ type: 'dog' },
]); // Iterable[{type: 'cat'}, {type: 'rat'}]
asyncFilter(predicate, source)
__asyncFilter(source, predicate)
See filter
flat(shouldFlat, depth, source)
flat({ shouldFlat, depth }, source)
flat(depth, source)
flat(source)
__flat(source, ?depth, ?shouldFlat)
Defaults:
depth
:1
shouldFlat
:value => isIterable(value) && !isString(value)
Yields each nested value from source
by recursing into values which are iterable -- up to the maximum recursion depth
. In additon to checking depth
, flat
will only recurse if the result of shouldFlat(value)
is truthy.
const nested = [
1,
[2, 3],
[
4,
[5, 6]
]
];
flat(nested); // Iterable[1, 2, 3, 4, Iterable[5, 6]]
flat(2, nested); // Iterable[1, 2, 3, 4, 5, 6]
const isString = value =>
typeof value === 'string' && value.length > 1;
flat(isString, Infinity, ['Hel', ['lo', '!']]); // Iterable['H', 'e', 'l', 'l', 'o', '!]
asyncFlat(shouldFlat, depth, source)
asyncFlat({ shouldFlat, depth }, source)
asyncFlat(depth, source)
asyncFlat(source)
__asyncFlat(source, ?depth, ?shouldFlat)
See flat
flatMap(func, source)
__flatMap(source, func)
For each value in source
, yields each value in predicate(value, idx)
. Equivalent to Array.prototype.flatMap
.
flatMap((x) => [x - 1, x], [1, 3, 5]); // Iterable[0, 1, 2, 3, 4, 5]
asyncFlatMap(func, source)
__asyncFlatMap(source, func)
See flatMap
interpose(value, source)
__interpose(source, value)
Yields value
between each of the values in source
.
interpose(null, [1, 2, 3]); // Iterable[1, null, 2, null, 3]
Note: If source
is a string you should instead use interposeSeq. A warning will be emitted if you do not.
asyncInterpose(value, source)
__asyncInterpose(source, value)
See interpose
interposeSeq(seq, source)
__interposeSeq(source, seq)
Yields values from seq
between each of the values in source
.
interposeSeq([0, 0], [1, 2, 3]); // Iterable[1, 0, 0, 2, 0, 0, 3]
asyncInterposeSeq(seq, source)
__asyncInterposeSeq(source, seq)
See interposeSeq
map(func, source)
__map(source, func)
For each value in source
, yields the result of predicate(value, idx)
. Equivalent to Array.prototype.map
.
map((x) => x * x, [0, 1, 2, 3]); // Iterable[0, 1, 4, 9]
asyncMap(func, source)
__asyncMap(source, func)
See map
prepend(value, source)
__prepend(source, value)
Yields value
followed by values from source
.
prepend(0, [1, 2, 3]); // Iterable[0, 1, 2, 3]
asyncPrepend(value, source)
__asyncPrepend(source, value)
See prepend
reverse(source)
__reverse(source)
Yields the values from iterable
in reverse order. If iterable
is not an array, this requires caching all its values in memory.
reverse([1, 2, 3]); // Iterable[3, 2, 1]
asyncReverse(source)
__asyncReverse(source)
See reverse
Note: Unlike reverse
, asyncReverse
will always make a cache of the entire input, even if the input is an array. If this is not acceptable, ensure that you use reverse
on arrays.
slice(start, end, step, source)
slice(start, end, source)
slice(start, source)
slice({ start, end, step }, source)
__slice(source, ?start, ?end, ?step)
Defaults:
end
:Infinity
step
:1
Yields a subsequence of values from source
, starting at index start
then advancing step
values, until it reaches index end
. The value at index end
will not be part of the result.
slice(0, 3, range(10)); // Iterable[0, 1, 2]
slice(2, range(10)); // Iterable[2, 3, 4, 5, 6, 7, 8, 9]
slice(2, 6, range(10)); // Iterable[2, 3, 4, 5]
slice(2, 6, 2, range(10)); // Iterable[2, 4]
start
and end
can also be negative. When they are, they refer to offsets from the end of iterable
, as they do in Array.prototype.slice
. This will always require consuming the entire iterable to figure out where the end is. step
must not be negative.
slice(0, -3, range(10)); // Iterable[0, 1, 2, 3, 4, 5, 6]
slice(-3, range(10)); // Iterable[7, 8, 9]
slice(-6, -2, range(10)); // Iterable[4, 5, 6, 7]
slice(-6, -2, 2, range(10)); // Iterable[4, 6]
When no arguments are passed to slice
it is functionally equivalent to wrap
.
asyncSlice(start, end, step, source)
asyncSlice(start, end, source)
asyncSlice(start, source)
asyncSlice({ start, end, step }, source)
__asyncSlice(source, ?start, ?end, ?step)
See slice
take(n, iterable)
__take(iterable, n)
Yields the first n
values from source
.
take(2, ['a', 'b', 'c']); // Iterable['a', 'b']
You should also consider using destructuring if you want individual named values and not an iterable of values. It is not necessary to use take
when destructuring. With destructuring the above would become:
const [first, second] = ['a', 'b', 'c'];
asyncTake(n, iterable)
__asyncTake(iterable, n)
See take
takeSorted(n, compare, source)
takeSorted(n, source)
takeSorted(compare, source)
takeSorted(source)
__takeSorted(source, ?n, ?compare)
Defaults:
comparator
: default comparator
Returns n
values from source
, sorted in ascending order according to comparator
. The function is both space efficient (only stores n
values) and fast (O(m log n)
), given m
as the total number of values in iterable
. It uses a heap internally.
takeSorted(3, [4, 5, 2, 3, 1]); // Iterable[1, 2, 3]
takeSorted([4, 5, 2, 3, 1]); // Iterable[1, 2, 3, 4, 5]
takeSorted(3, (a, b) => b - a, [4, 5, 2, 3, 1]); // Iterable[5, 4, 3]
asyncTakeSorted(n, compare, source)
asyncTakeSorted(n, source)
asyncTakeSorted(compare, source)
asyncTakeSorted(source)
__asyncTakeSorted(source, ?n, ?compare)
See takeSorted
takeWhile(predicate, source)
__takeWhile(source, predicate)
Returns values from source
, starting at the beginning up until the first value for which the result of predicate(value, idx)
is falsy.
takeWhile(isEven, [2, 4, 1, 3]); // Iterable[2, 4]
takeWhile(isEven, [1, 2, 3, 4]); // Iterable[]
asyncTakeWhile(predicate, source)
__asyncTakeWhile(source, predicate)
See takeWhile
tap(callback, source)
__tap(source, callback)
For each value in source
, executes callback(value, idx)
and yields the value (unmodified). Note that while this looks similar to what a for..of
loop or forEach
method might do, the key difference is that tap
does not force evaluation of the iterable.
pipeExec(
[0, 1, 2],
filter((value) => !!value),
tap((value) => console.log(value)),
map((value) => value + 1),
); // Logs 1, 2 and returns Iterable[2, 3]
asyncTap(callback, source)
__asyncTap(source, callback)
See tap
window(size, source)
__window(source, size)
For values in source
, yields a window
iterable of size size
which starts with that value and also contains the next values from source
. The window
instance is the same on every iteration. Only emits full windows, which means fewer windows will be emitted than there are values in source
. If you need a window for every value in source
, use windowAhead or windowBehind.
window(3, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[1, 2, 3],
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// ]
window(5, [1, 2, 3]);
// Iterable[]
asyncWindow(size, source)
__asyncWindow(source, size)
See window
windowAhead({ filler, useFiller }, size, source)
windowAhead(size, source)
__windowAhead(source, size, ?{ filler, useFiller })
Defaults:
filler
:undefined
useFiller
:true
For every value in source
, yields an iterable window
of size size
which starts with that value and also contains the next values from source
. The window
instance is the same on every iteration. When there are not enough additional values in source
to fill the window, filler
will be used in place of the missing values. Alternatively if useFiller
is false
, missing values will create windows smaller than size
.
windowAhead(3, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[1, 2, 3],
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// Iterable[4, 5, undefined]
// Iterable[5, undefined, undefined]
// ]
windowAhead(3, { filler: Infinity }, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[1, 2, 3],
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// Iterable[4, 5, Infinity]
// Iterable[5, Infinity, Infinity]
// ]
windowAhead(3, { useFiller: false }, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[1, 2, 3],
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// Iterable[4, 5]
// Iterable[5]
// ]
asyncWindowAhead({ filler, useFiller }, size, source)
asyncWindowAhead(size, source)
__asyncWindowAhead(source, size, ?{ filler, useFiller })
See windowAhead
windowBehind({ filler }, size, source)
windowBehind(size, source)
__windowBehind(source, size, ?{ filler })
Defaults:
filler
:undefined
For every value in source
, yields a window
iterable of size size
which contains the values leading up to and including that value. The window
instance is the same on every iteration. When there are not enough prior values to fill the window, filler
will be used in place of the missing values.
windowBehind(3, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[undefined, undefined, 1],
// Iterable[undefined, 1, 2]
// Iterable[1, 2, 3]
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// ]
windowBehind(3, { filler: 0 }, [1, 2, 3, 4, 5]);
// Iterable[
// Iterable[0, 0, 1]
// Iterable[0, 1, 2]
// Iterable[1, 2, 3]
// Iterable[2, 3, 4]
// Iterable[3, 4, 5]
// ]
asyncWindowBehind({ filler }, size, source)
asyncWindowBehind(size, source)
__asyncWindowBehind(source, size, ?{ filler })
See windowBehind
wrap(source)
Yields the values from source
. Its main purposes include allowing nullable iterables to be treated as non-null iterables, and to give arbitrary iterables the semantics of iter-tools iterables.
const maybeIterable =
Math.random() > 0.5 ? [1, 2, 3] : null;
[...wrap(maybeIterable)]; // [1, 2, 3] OR []
asyncWrap(source)
See wrap
Also turns sync iterables into async iterables and ensures async next()
queueing semantics.
await asyncWrap([1, 2, 3])[Symbol.asyncIterator]().next(); // { value: 1, done: false }
batch(size, source)
__batch(source, size)
Yields non-overlapping subsequences each containing size
values from source
.
batch(2, range(5)); // [0, 1], [2, 3], [4]
asyncBatch(size, source)
__asyncBatch(source, size)
See batch
bisect(at, source)
__bisect(source, at)
Yields two part
subsequences of source
. The split position is chosen with at
. If at
is a number, the first part will contain that number of values. If at
is a negative number the second part will contain that number of values. If at
is a function the split will be before the first value
for which the result of at(value, idx)
is truthy.
bisect
is specially designed to work with destructuring, but this comes at a cost: for resources to be released properly you must use the second half of the split. If you only need the first half you must instead use take or takeWhile. For example instead of const [seq] = bisect(cond, source)
you must write const seq = takeWhile((v, i) => !cond(v, i), source)
.
const source = [-2, -1, 0, 1, 2];
const [negatives, positives] = bisect(
(i) => i >= 0,
source,
);
negatives; // Iterable[-2, -1]
positives; // Iterable[0, 1, 2]
const [
firstThree, // Iterable[0, 1, 2]
others, // Iterable[3, 4, 5, 6, 7, 8, 9]
] = bisect(3, range(10));
const [, lastThree] = bisect(-3, range(10));
lastThree; // Iterable[7, 8, 9]
asyncBisect(at, source)
__asyncBisect(source, at)
Synchronously yields two async part
subsequences of source
. This means that it is still possible to use the result with destructuring, e.g. const [first, second] = asyncBisect(source)
. For more on how the split is performed (and how the result can be used) see bisect.
Yields each value in source
as an iterable of one value.
split([1, 2, 3]); // Iterable[Iterable[1], Iterable[2], Iterable[3]]
asyncSplit(source)
__asyncSplit(source)
See split
splitGroups(getKey, source)
splitGroups(source)
__splitGroups(source, ?getKey)
Defaults:
getKey
:(value) => value
Yields a PartsIterable of [key
, group
] pairs from source
, where group
is a subsequence of values
from source
for which every value
has the same key
as returned by getKey(value, idx)
(as compared with ===
).
splitGroups(Math.abs, [1, 1, -1, -1, 4, -1]);
// Iterable [
// [1, Iterable[1, 1, -1, -1]]
// [4, Iterable[4]]
// [1, Iterable[-1]]
// ]
asyncSplitGroups(getKey, source)
asyncSplitGroups(source)
__asyncSplitGroups(source, ?getKey)
See splitGroups
splitOn(same, separator, source)
splitOn(separator, source)
__splitOn(source, separator, ?same)
Defaults:
same
:Object.is
Yields a PartsIterable of parts from source
, where separatorValue
is used to mark the boundary between parts in source
. separatorValue
will not occur in the output. Two values are considered to be the same if the result of same(a, b)
is truthy.
splitOn(null, [1, null, 2, null, 3]); // Iterable[[1], [2], [3]]
Note: If source
is a string you should instead use splitOnSeq. A warning will be emitted if you do not.
asyncSplitOn(same, separator, source)
asyncSplitOn(separator, source)
__asyncSplitOn(source, separator, ?same)
See splitOn
splitOnAny(same, separators, source)
splitOnAny(separators, source)
__splitOnAny(source, separators, ?same)
Defaults:
same
:Object.is
Yields a PartsIterable of parts from source
, where separatorValues
are used to mark the boundary between parts in source
. None of the separatorValues
will not occur in the output. Two values are considered to be the same if the result of same(a, b)
is truthy.
splitOnAny([null, undefined], [1, null, 2, undefined, 3]); // Iterable[[1], [2], [3]]
Note: If source
is a string you should instead use splitOnAnySeq. A warning will be emitted if you do not.
asyncSplitOnAny(same, separators, source)
asyncSplitOnAny(separators, source)
__asyncSplitOnAny(source, separators, ?same)
See splitOnAny
splitOnAnySeq(same, separatorSeqs, source)
splitOnAnySeq(separatorSeqs, source)
__splitOnAnySeq(source, separatorSeqs, ?same)
Defaults:
same
:Object.is
Yields a PartsIterable of parts from source
, where separatorSeqs
are used to mark the boundary between parts in source
. When any separatorSeq
in separatorSeqs
is matched, all matched values are consumed from source
and will not appear in any part
, nor may they be part of any other separatorSeq
match. Matches greedily, which is to say the longest possible separator match will be prioritized. Two values are considered to be the same if the result of same(a, b)
is truthy.
splitOnAnySeq(
[['\n'], ['\r\n']],
'mixed\r\nline\nterminators',
); // Iterable['mixed', 'line', 'terminators']
asyncSplitOnAnySeq(same, separatorSeqs, source)
asyncSplitOnAnySeq(separatorSeqs, source)
__asyncSplitOnAnySeq(source, separatorSeqs, ?same)
See splitOnAnySeq
splitOnSeq(same, separatorSeq, source)
splitOnSeq(separatorSeq, source)
__splitOnSeq(source, separatorSeq, ?same)
Defaults:
same
:Object.is
Yields a PartsIterable of parts from source
, where separatorSeq
is used to mark the boundary between parts in source
. When separatorSeq
is matched, all matched values are consumed from source
. They will not appear in any part
, nor may they be part of any other separatorSeq
match. Two values are considered to be the same if the result of same(a, b)
is truthy.
splitOnSeq([0, 0], [1, 0, 0, 2, 0, 0, 3]); // Iterable[[1], [2], [3]]
//`separatorSeq` is in the result because separators overlap in `source`.
splitOnSeq([0, 0], [0, 0, 0, 1, 2]); // Iterable[[], [0, 1, 2]]
asyncSplitOnSeq(same, separatorSeq, source)
asyncSplitOnSeq(separatorSeq, source)
__asyncSplitOnSeq(source, separatorSeq, ?same)
See splitOnSeq
splitWhen(predicate, source)
__splitWhen(source, predicate)
Yields a PartsIterable of parts from source
, a value
from source
for which the result of predicate(value, idx)
is truthy is considered a separator, and will not occur in the output. If source
is a string you may also specify a regex predicate, in which case the behavior will match str.split(RegExp)
. This is the only situation in which you will be able to match more than one value from source
at a time.
splitWhen(
x => x == null,
[1, null, 2, undefined, 3]
); // Iterable[Iterable[1], Iterable[2], Iterable[3]]
splitWhen(',', 'foo,bar,baz'); // Iterable['foo', 'bar', 'baz']
splitWhen(/, /, 'foo, bar, baz'); // Iterable['foo', 'bar', 'baz']
asyncSplitWhen(predicate, source)
__asyncSplitWhen(source, predicate)
See splitWhen
collate(compare, ...sources)
__collate(sources, compare)
Combines values from each source
in sources
into a single iterable, peserving the ordering of values within each source
. Collate uses comparator
to establish a partial ordering of values at the head of each source
. At each step it yields the lowest value in the ordering then recomputes the ordering.
collate([1, 2, 5, 6], [3, 4]); // Iterable[1, 2, 3, 4, 5, 6]
collate((a, b) => b - a, [6, 5, 2, 1], [4, 3]); // Iterable[6, 5, 4, 3, 2, 1]
asyncCollate(compare, ...sources)
__asyncCollate(sources, compare)
See collate
compress(source, included)
__compress(source, included)
Consumes values from source
and included
iterables in parallel, at each step yielding the source
value if the included
value is truthy.
compress([0, 1, 2, 3, 4], [0, 0, 1, 1]); // 2, 3
compress([0, 1, 2, 3, 4], cycle([true, false])); // 0, 2, 4
asyncCompress(source, included)
__asyncCompress(source, included)
See compress
concat(...sources)
__concat(...sources)
Yields each value from each source
in sources
. First all values from the first source
are yielded, then then from the second, etc.
concat([3, 5, 6], [1, 1], [10]); // 3, 5, 6, 1, 1, 10
asyncConcat(...sources)
__asyncConcat(...sources)
See concat
asyncInterleaveReady(...sources)
__asyncInterleaveReady(sources)
Interleaves values from each source
in sources
, yielding values in whatever order they resolve (become ready). Note that this means that the results of this interleave will usually not be repeatable.
asyncInterleaveReady(aValues, bValues);
join(source)
__join(source)
Given source
, an iterable of iterables, yields all values from each iterable. It is the inverse of split
.
join([[1], [2], [3]]); // Iterable[1, 2, 3]
asyncJoin(source)
__asyncJoin(source)
See join
joinWith(separator, source)
__joinWith(source, separator)
Given source
, an iterable of iterables, yields all values from each iterable with separator
in between. It is the inverse of splitOn
.
joinWith(null, [[1], [2], [3]]); // Iterable[1, null, 2, null, 3]
asyncJoinWith(separator, source)
__asyncJoinWith(source, separator)
See joinWith
joinWithSeq(separatorSeq, source)
__joinWithSeq(source, separatorSeq)
Given source
, an iterable of iterables, yields all values from each iterable with the separatorSeq
values in between. It is the inverse of splitOnSeq
.
joinWithSeq([null, null], [[1], [2], [3]]); // Iterable[1, null, null, 2, null, null, 3]
asyncJoinWithSeq(separatorSeq, source)
__asyncJoinWithSeq(source, separatorSeq)
See joinWithSeq
roundRobin(start, step, ...sources)
roundRobin(step, ...sources)
roundRobin(...sources)
__roundRobin(sources, ?step, ?start)
Defaults:
start
:0
step
:1
Combines values from each source
in sources
into a single iterable, peserving the ordering of values within each source
. First yields a value from the source
with index start
, then one from the source
with index start + step
, and so on, wrapping the indexes of sources using % sources.length
.
roundRobin([1, 3, 5], [2, 4, 6]); // Iterable[1, 2, 3, 4, 5, 6]
roundRobin(2, [1, 4], [3, 6], [2, 5]); // Iterable[1, 2, 3, 4, 5, 6]
roundRobin({ start: 1, step: 1 }, [2, 4, 6], [1, 3, 5]); // Iterable[1, 2, 3, 4, 5, 6]
asyncRoundRobin(start, step, ...sources)
asyncRoundRobin(step, ...sources)
asyncRoundRobin(...sources)
__asyncRoundRobin(sources, ?step, ?start)
See roundRobin
zip(...sources)
__zip(sources)
Consumes each source
in sources
in parallel, at each step yielding an array with one value from every source
. Stops when the shortest source iterable is exausted.
zip([1, 2], [3, 4], [5, 6, 7]); // [1, 3, 5], [2, 4, 6]
asyncZip(...sources)
__asyncZip(sources)
See zip
zipAll({ filler }, ...sources)
zipAll(...sources)
__zipAll(sources, ?{ filler })
Defaults:
filler
:undefined
Consumes each source
in sources
in parallel, at each step yielding an array with one value from every source
. Stops when the shortest source iterable is exausted. Where some sources
are exhausted before all sources
are exchausted, filler
will be used in place of the missing values.
zipAll([1, 2], [3, 4], [5, 6, 7]); // [1, 3, 5], [2, 4, 6], [undefined, undefined, 7]
zipAll({ filler: null }, [1, 2], []); // [1, null], [2, null]
asyncZipAll({ filler }, ...sources)
asyncZipAll(...sources)
__asyncZipAll(sources, ?{ filler })
See zipAll
deepEqual(...values)
__deepEqual(values, ?same, ?coerceNil)
Defaults:
same
:Object.is
coerceNil
:true
Returns true
if all values
are deepEqual to each other and false
otherwise. Values are considered equal if they are iterables containing the same values, or if the result of same(a, b, depth)
is truthy. depth
represents the number of iterables wrapping the value. If coerceNil
is true
then null
and undefined
are considered to be iterables.
Note: deepEqual
does not consider strings to be iterables. That would cause infinite recursion.
deepEqual([1, 2, 3], [1, 2, 3], [1, 2, 3]); // true
deepEqual([[1, 2, 3]], [[1, 2, 3]], [[1, 2, 3]]); // true
deepEqual(1, 1, 1); // true
deepEqual(null, [], ''); // true
deepEqual([1, 2, 3], [3, 2, 1]); // false
Note that in order to avoid ambiguity comparator
can only be passed to __deepEqual
. If you need it just write this:
function same(a, b) {
return a.toUpperCase() === b.toUpperCase();
}
function myEqual(...values) {
return __deepEqual(values, same, false);
}
myEqual('foo', 'FOO'); // true
myEqual(null, ''); // false
asyncDeepEqual(...values)
__asyncDeepEqual(values, ?same, ?coerceNil)
See deepEqual
equal(same, ...iterables)
equal(...iterables)
__equal(iterables, ?same)
Defaults:
same
:Object.is
Returns true
if all iterables
consist of the same sequence of values. Otherwise returns false
. Two values are considered to be the same if the result of same(a, b)
is truthy.
equal([1, 2, 3], [1, 2, 3], [1, 2, 3]); // true
Note: If source
is a string you should instead use startsWithSeq. A warning will be emitted if you do not.
asyncEqual(same, ...iterables)
asyncEqual(...iterables)
__asyncEqual(iterables, ?same)
See equal
every(predicate, iterable)
__every(iterable, predicate)
Returns true
if, for every value in source
, the result of predicate(value, idx)
is truthy. Otherwise returns false
.
every(isEven, [1, 2, 3]); // returns false
every(isEven, [2, 4, 6]); // returns true
asyncEvery(predicate, iterable)
__asyncEvery(iterable, predicate)
See every
find(predicate, iterable)
__find(iterable, predicate)
Returns the first value in iterable
for which predicate(value, idx)
returns a truthy value. It is the equivalent of Array.prototype.find
.
find((animal) => animal.kind === 'dog', [
{ type: 'cat' },
{ type: 'dog' },
]); // {type: 'dog'}
asyncFind(predicate, iterable)
__asyncFind(iterable, predicate)
See find
findOr(notFoundValue, func, iterable)
__findOr(iterable, notFoundValue, func)
Returns the first value in iterable
for which predicate(value, idx)
returns a truthy value, or notFoundValue
if no value satisfied the predicate.
findOr(0, (x) => x > 10, [1, 2, 3]); // 0
asyncFindOr(notFoundValue, func, iterable)
__asyncFindOr(iterable, notFoundValue, func)
See findOr
first(iterable)
__first(iterable)
Returns the first value from iterable
, or undefined
when iterable
is empty.
first([1, 2, 3]); // 1
first([]); // undefined
asyncFirst(iterable)
__asyncFirst(iterable)
See first
firstOr(whenEmpty, iterable)
__firstOr(iterable, whenEmpty)
Returns the first value from iterable
, or whenEmpty
when iterable
is empty.
firstOr(0, [1, 2, 3]); // 1
firstOr(0, []); // 0
asyncFirstOr(whenEmpty, iterable)
__asyncFirstOr(iterable, whenEmpty)
See firstOr
includes(same, value, iterable)
includes(value, iterable)
__includes(iterable, value, ?same)
Defaults:
same
:Object.is
Returns true
if iterable
includes the specified value
, or false
otherwise. Two values are considered to be the same if the result of same(a, b)
is truthy.
includes(2, [1, 2, 3]); // true
includes(0, [1, 2, 3]); // false
Note: If source
is a string you should instead use includesSeq. A warning will be emitted if you do not.
asyncIncludes(same, value, iterable)
asyncIncludes(value, iterable)
__asyncIncludes(iterable, value, ?same)
See includes
includesAny(same, values, iterable)
includesAny(values, iterable)
__includesAny(iterable, values, ?same)
Defaults:
same
:Object.is
Returns true
if iterable
includes any of the specified values
, or false
otherwise. Two values are considered to be the same if the result of same(a, b)
is truthy.
includesAny([0, 1], [1, 2, 3]); // true
includesAny([0, 1], [2, 3, 4]); // false
Note: If source
is a string you should instead use includesAnySeq. A warning will be emitted if you do not.
asyncIncludesAny(same, values, iterable)
asyncIncludesAny(values, iterable)
__asyncIncludesAny(iterable, values, ?same)
See includesAny
includesAnySeq(same, seqs, iterable)
includesAnySeq(seqs, iterable)
__includesAnySeq(iterable, seqs, ?same)
Defaults:
same
:Object.is
Returns true
if any of the the seqs
(subsequences) of values can be found somewhere in iterable
, or false
otherwise. Two values are considered to be the same if the result of same(a, b)
is truthy.
includesAnySeq(
[
[1, 2],
[2, 3],
],
[1, 2, 3],
); // true
includesAnySeq(
[
[2, 3],
[3, 4],
],
[1, 2, 3],
); // true
includesAnySeq(
[
[0, 1],
[3, 4],
],
[1, 2, 3],
); // false
asyncIncludesAnySeq(same, seqs, iterable)
asyncIncludesAnySeq(seqs, iterable)
__asyncIncludesAnySeq(iterable, seqs, ?same)
See includesAnySeq
includesSeq(same, seq, iterable)
includesSeq(seq, iterable)
__includesSeq(iterable, seq, ?same)
Defaults:
same
:Object.is
Returns true
if the seq
(subsequence) of values can be found somewhere in iterable
, or false
otherwise. Two values are considered to be the same if the result of same(a, b)
is truthy.
includesSeq([1, 2], [1, 2, 3]); // true
includesSeq([1, 2, 3], [1, 2, 3]); // true
includesSeq([2, 3, 4], [1, 2, 3]); // false
asyncIncludesSeq(same, seq, iterable)
asyncIncludesSeq(seq, iterable)
__asyncIncludesSeq(iterable, seq, ?same)
See includesSeq
isEmpty(iterable)
__isEmpty(iterable)
Returns true
if iterable
contains no values, and false
otherwise.
isEmpty([]); // true
isEmpty(null); // true
isEmpty(range(1)); // false
isEmpty([undefined]); // false
asyncIsEmpty(iterable)
__asyncIsEmpty(iterable)
See isEmpty
isObject(value)
Returns typeof value === 'object' && value !== null
. Note that iterables are objects, so it is expected that the most common way to use this method will to to first eliminate the possibility that value
is an iterable (e.g. using notIterable). Type-safe in typescript.
Note: lodash has a popular method of the same name, which treats functions as objects as well. This method is what lodash calls isObjectLike
.
isObject({}); // true
isObject([]); // true
isObject(new Date()); // true
isObject(new (class Foo {})()); // true
isObject(null); // false
isObject(undefined); // false
isObject(Date); // false (function)
isObject(class Foo {}); // false (function)
isSorted(compare, iterable)
isSorted(iterable)
__isSorted(iterable, ?compare)
Returns true
if the values in iterable
are sorted in ascending order according to comparator
, and false
otherwise.
isSorted([1, 2, 3]); // true
isSorted((a, b) => b - a, [3, 2, 1]); // true
asyncIsSorted(compare, iterable)
asyncIsSorted(iterable)
__asyncIsSorted(iterable, ?compare)
See isSorted
notObject(value)
Returns true
if value
is not an object, and true
otherwise. For details see the method's inverse: isObject. Type-safe in typescript.
notObject({}); // false
notObject([]); // false
notObject(new Date()); // false
notObject(new (class Foo {})()); // false
notObject(null); // true
notObject(undefined); // true
notObject(Date); // true
notObject(class Foo {}); // true
reduce(initial, reducer, iterable)
reduce(reducer, iterable)
__reduce(iterable, reducer, ?initial)
Defaults:
initial
:first(iterable)
Turns iterable
into a single result
value using a reducer function. For each value
in iterable
, calls reducer(result, value, idx)
, where result result
is either the initial
for the first value in iterable
, otherwise the value returned from the last call to reducer
. It is equivalent to Array.prototype.reduce
.
reduce(0, (result, v) => result + v, [1, 2, 3]); // 6
reduce(10, (result, v) => result + v, [1, 2, 3]); // 16
reduce((result, v) => result + v, [1, 2, 3]); // 6
asyncReduce(initial, reducer, iterable)
asyncReduce(reducer, iterable)
__asyncReduce(iterable, reducer, ?initial)
See reduce
size(iterable)
__size(iterable)
Returns the number of values in iterable
by iterating over it. This is more work than is neccessary for any concrete type like Array
, Map
, or Set
. If you know your data is one of those types, use getSize instead.
size([1, 2, 3]); // 3
asyncSize(iterable)
__asyncSize(iterable)
See size
some(func, iterable)
__some(iterable, func)
Returns true
if the result of predicate(value, idx)
is truthy for at least one value in iterable
, and false
otherwise.
some(isEven, [1, 2, 3]); // true
some(isEven, [1, 3, 7]); // false
asyncSome(func, iterable)
__asyncSome(iterable, func)
See some
startsWith(same, value, iterable)
startsWith(value, iterable)
__startsWith(iterable, value, ?same)
Defaults:
same
:Object.is
Returns true
if the first value in source
is value
. Otherwise returns false
. Two values are considered to be the same if the result of same(a, b)
is truthy.
startsWith(1, [1, 2, 3]); // true
Note: If source
is a string you should instead use startsWithSeq. A warning will be emitted if you do not.
asyncStartsWith(same, value, iterable)
asyncStartsWith(value, iterable)
__asyncStartsWith(iterable, value, ?same)
See startsWith
startsWithAny(same, values, iterable)
startsWithAny(values, iterable)
__startsWithAny(iterable, values, ?same)
Defaults:
same
:Object.is
Returns true
if the first value in source
is any value
in values
. Otherwise returns false
. Two values are considered to be the same if the result of same(a, b)
is truthy.
startsWithAny([0, 1], [1, 2, 3]); // true
Note: If source
is a string you should instead use startsWithAnySeq. A warning will be emitted if you do not.
asyncStartsWithAny(same, values, iterable)
asyncStartsWithAny(values, iterable)
__asyncStartsWithAny(iterable, values, ?same)
See startsWithAny
startsWithAnySeq(same, seqs, iterable)
startsWithAnySeq(seqs, iterable)
__startsWithAnySeq(iterable, seqs, ?same)
Defaults:
same
:Object.is
Returns true
if the first subsequence of values in source
match any valueSeq
in valueSeqs
. Otherwise returns false
. Two values are considered to be the same if the result of same(a, b)
is truthy.
startsWithAnySeq(
[
[0, 1],
[1, 2],
],
[1, 2, 3],
); // true
asyncStartsWithAnySeq(same, seqs, iterable)
asyncStartsWithAnySeq(seqs, iterable)
__asyncStartsWithAnySeq(iterable, seqs, ?same)
See startsWithAnySeq
startsWithSeq(same, seq, iterable)
startsWithSeq(seq, iterable)
__startsWithSeq(iterable, seq, ?same)
Defaults:
same
:Object.is
Returns true
if the first subsequence of values in source
matches valueSeq
. Otherwise returns false
. Two values are considered to be the same if the result of same(a, b)
is truthy.
startsWithSeq([1, 2], [1, 2, 3]); // true
asyncStartsWithSeq(same, seq, iterable)
asyncStartsWithSeq(seq, iterable)
__asyncStartsWithSeq(iterable, seq, ?same)
See startsWithSeq
See stingFrom
asyncStr(chars)
__asyncStr(chars)
See stringFromAsync
takeLast(iterable)
__takeLast(iterable)
Returns the last value from iterable
, or undefined
when iterable
is empty.
Performance note: this requires consuming the entire iterable. If iterable
is an array this method will do a lot of unnecessary work compared to arrayLast(array)
.
takeLast([1, 2, 3]); // 3
takeLast([]); // undefined
asyncTakeLast(iterable)
__asyncTakeLast(iterable)
See takeLast
takeLastOr(whenEmpty, iterable)
__takeLastOr(iterable, whenEmpty)
Returns the last value from iterable
, or whenEmpty
when iterable
is empty.
Performance note: this requires consuming the entire iterable. If iterable
is an array this method will do a lot of unnecessary work compared to arrayLastOr(array, whenEmpty)
.
takeLastOr(0, [1, 2, 3]); // 3
takeLastOr(0, []); // 0
asyncTakeLastOr(whenEmpty, iterable)
__asyncTakeLastOr(iterable, whenEmpty)
See takeLastOr
asyncBuffer(source, n)
__asyncBuffer(source, n)
Returns a singleton async iterable iterator which yields the same values as source
. For every value the next n
values also start their computation in parallel. It may or may not be possible for useful work to be done in parallel depending on the nature of source
.
An example of a situation in which it is possible to parallelize is when you have a series of expensive requests to make and you know in advance what they will be:
pipe(
range,
asyncMap(i => fetch(`/page/${i}`).then(res => res.json()),
asyncSlice(0, 50),
asyncBuffer(4),
toArray
);
The above code could fetch four pages at once, potentially greatly speeding up the process when compared code that did not use asyncBuffer
.
It is important to note that the method will always try to buffer past the end of your iterable. This is a design limitation of async iterators. This is usually fine, but makes it unwise to specify extremely high values for n
. asyncBuffer(Infinity, source)
for example would simply be an infinite loop. For this reason all values of n >= 1024
are forbidden. This is expected to be a very permissive limit. In practice n
should probably be in the range of 2
to 16
. You must recall that Javascript is fundamentally single threaded, so having more CPU cores will not help you execute such "parallelized" code any faster.
Here is a fuller example demonstrating the mechanics of asyncBuffer
:
const delay = (ms) =>
new Promise((resolve) => setTimeout(resolve, ms));
const source = asyncMap(
(_) => new Promise((resolve) => setTimeout(resolve, 200)),
range(),
);
const buffered = asyncBuffer(6, source); // Values start buffering
await delay(800));
// Four values are already buffered here
await buffered.next(); // ~0ms
await buffered.next(); // ~0ms
await buffered.next(); // ~0ms
await buffered.next(); // ~0ms
// After this point values are being requested as fast as they
// can possibly be fulfilled, so buffer offers no additional benefits.
await buffered.next(); // ~200ms
await buffered.next(); // ~200ms
// But if additional delays are incurred in processing values,
// it has value again!
await delay(300);
await buffered.next(); // ~0ms
await buffered.next(); // ~100ms
await buffered.next(); // ~200ms
// ...
asyncThrottle(intervalMs, source)
__asyncThrottle(source, intervalMs)
Rate-limits source
, ensuring that requests for the next value in source
are made at intervals of no less than intervalMs
milliseconds. Yields the same values in the same order as source
.
async function* pollHealth() {
while (true) {
yield fetch('/health').json();
}
}
for (const response of asyncThrottle(100, pollHealth)) {
// This happens only once every 100 milliseconds
renderHealth(response);
}
Returns an iterable of forks
of source
. Each fork contains the same values as source
, and can be consumed independently. This works even if source
cannot itself be consumed more than once, for example because it is a generator. Values are buffered until they have been consumed by all forks. Each fork can only be consumed once. Note that fork caches values, and these cached values can never be released until forks.return()
is called on the iterable of forks. If you are consuming forks
using destructuring syntax (as in this example) or a for..of
loop, this is done for you.
const [forkA, forkB, forkC] = fork(function* () {
yield 1;
yield 2;
yield 3;
});
forkA.next().value; // 1
forkB.next().value; // 1
forkC.next().value; // 1
forkA.next().value; // 2
forkB.next().value; // 2
forkC.next().value; // 2
forkA.next().value; // 3
forkB.next().value; // 3
forkC.next().value; // 3
WARNING
There is a really good chance that you'd be better off using toArray
to cache an iterable instead of fork
. fork
is only better when you have multiple consumers of an infinite (or very large) iterable, and you are sure that they will be consuming in parallel.
asyncFork(source)
__asyncFork(source)
See fork
Note: Returns an iterable (sync) of async iterables.
arrayFrom(strings)
Aliases: toArray
Transform source
into an array. Roughly equivalent to Array.from
, except that it turns null
and undefined
into []
. Since arrays are iterable, this method can also be thought of as a way to cache all values in an iterable.
arrayFrom(slice(0, 3, range())); // [1, 2, 3]
arrayFrom(null); // []
arrayFromAsync(source)
Aliases: asyncToArray
Transform an async source
into an array. Since arrays are valid inputs to async methods, can also be thought of as a way to cache all values in an async iterable.
await arrayFromAsync(asyncWrap(slice(0, 3, range()))); // [1, 2, 3]
await arrayFromAsync(null); // []
consume(iterable)
__consume(iterable)
Consumes iterable
.
function* log123() {
console.log('1');
yield;
console.log('2');
yield;
console.log('3');
}
consume(log123); // prints 1 2 3
asyncConsume(iterable)
__asyncConsume(iterable)
See consume
forEach(callback, iterable)
__forEach(iterable, callback)
Calls callback(value, idx)
for each value in iterable
. Note that as a consuming method, forEach
is not lazy. It will trigger evaluation of iterable
.
forEach((value) => console.log(value), [1, 2, 3]); // prints 1, 2, 3
forEach((value) => console.log(value), null); //
asyncForEach(callback, iterable)
__asyncForEach(iterable, callback)
See forEach
objectFrom(entries, ?prototype)
Aliases: toObject
Transforms an entries
iterable into an object. Each entry should be of the form [key, value]
. Roughly equivalent to Object.fromEntries
, except that it turns null
and undefined
into {}
. An optional prototype
will be passed to Object.create
if specified.
objectFrom([
['droids', ['R2', '3PO']],
['people', ['Luke', 'Leia', 'Han']],
]); // { droids: ['R2', '3PO'], people: ['Luke', 'Leia', 'Han'] }
objectFrom(null); // {}
objectFromAsync(entries, ?prototype)
Aliases: asyncToObject
Transform an async entries
iterable (or a sync one) into an object. Each entry should be of the form [key, value]
. An optional prototype
will be passed to Object.create
if specified.
objectFromAsync(
asyncWrap([
['droids', ['R2', '3PO']],
[('people': ['Luke', 'Leia', 'Han'])],
]),
); // { droids: ['R2', '3PO'], people: ['Luke', 'Leia', 'Han'] }
await objectFromAsync(null); // []
stringFrom(source)
Aliases: str
Concatenate chars
into a string.
stringFrom(['a', 'b', 'c', 'def']); // 'abcdef'
stringFrom(null); // ''
stringFromAsync(strings)
Aliases: asyncStr
See stringFrom.
toArray(source)
__toArray(source)
See arrayFrom
asyncToArray(source)
__asyncToArray(source)
See arrayFromAsync
toObject(iterable, ?proto)
__toObject(iterable, ?proto)
See objectFrom
asyncToObject(iterable, ?proto)
__asyncToObject(iterable, ?proto)
See objectFromAsync
isAsyncIterable(value)
Returns true
if value
has a Symbol.asyncIterator
property and false
otherwise. Type-safe in typescript.
isAsyncIterable((async function* () {})()); // true
isAsyncIterable((function* () {})()); // false
isAsyncIterable([]); // false
isAsyncIterable({}); // false
isAsyncIterable(undefined); // false
isAsyncIterable(null); // false
isAsyncLoopable(value)
Returns true
if value
has a Symbol.asyncIterator
or Symbol.iterator
property and false
otherwise. If isAsyncLoopable(value)
then value
may be used as the subject of a for await..of
loop. Type-safe in typescript.
isAsyncLoopable((async function* () {})()); // true
isAsyncLoopable((function* () {})()); // true
isAsyncLoopable([]); // true
isAsyncLoopable({}); // false
isAsyncLoopable(undefined); // false
isAsyncLoopable(null); // false
isAsyncWrappable(value)
Returns true
if value
isAsyncIterable, isIterable, or isNil (and false
otherwise). When isAsyncWrappable(value)
, it is safe to pass value to asyncWrap as well as other methods that take an AsyncWrappable, which is usually named iterable
or source
. Type-safe in typescript.
isAsyncWrappable((async function* () {})()); // true
isAsyncWrappable((function* () {})()); // true
isAsyncWrappable([]); // true
isAsyncWrappable(undefined); // true
isAsyncWrappable(null); // true
isAsyncWrappable({}); // false
isAsyncWrappable(4); // false
isIterable(value)
Returns true
if value
is iterable (which is to say it has a Symbol.iterator
property) and false
otherwise. Iterables are inputs (often named source
or iterable
) to most iter-tools
methods, so it is useful to know all the ways you can create them:
Javascript's builtin data types are iterable:
isIterable([]); // true
isIterable(new Map()); // true
isIterable(new Set()); // true
Any class can be iterable if it defines a Symbol.iterator
method. Note that something similar works just as well if you are still constructing your prototype chains without the help of the class
keyword.
class MyClass {
constructor(data = []) {
this.data = data;
},
[Symbol.iterator]() {
return this.data[Symbol.iterator]();
}
}
isIterable(new MyClass())
The result of calling a generator function is an iterable iterator. Generator functions are highly useful implementing any kind of operation. Most iter-tools
are implemented using them internally.
function* range() {
for (let i = 0; ; i++) yield i;
}
isIterable(range()); // true
All iterators should also be iterables. This can be achieved by returning this
from the Symbol.iterator
method like so:
const yesIterator = {
next() {
return { value: 'yes', done: false }
}
[Symbol.iterator]() {
return this;
}
}
isIterable(yesIterator); // true
Many iterators use return this
(as above) to ensure they can be used anywhere an iterable can be. This means you can write confusing things like [Symbol.iterator]()[Symbol.iterator]()
. Beware however! While this is often safe you must remember that the value returned by [Symbol.iterator]()
is not required to be an iterable: it must only be an iterator.
Other kinds of values are not iterable, though iter-tools
chooses to allow null
and undefined
in most places an iterable is expected.
isIterable(undefined); // false
isIterable(null); // false
isIterable(42); // false
isIterable({}); // false
Note: isIterable
does not check to make sure that Symbol.iterator
is a method. Code in which Symbol.iterator
is not a method is always incorrect, and attempted usage of such an "iterable" will trigger an appropriate error at the language level.
See isIterable. For sync iterables these methods share the same implementation.
isNil(value)
Returns true
if value
is null
or undefined
and false
otherwise. Type-safe in typescript.
isNil(undefined); // true
isNil(null); // true
isNil(0); // false
isNil({}); // false
isNil(NaN); // false
isNull(value)
Returns true
if value
is null
and false
otherwise. Type-safe in typescript.
isNull(null); // true
isNull(undefined); // false
isUndefined(value)
Returns true
if value
is undefined
and false
otherwise. Implemented using typeof
. Type-safe in typescript.
isUndefined(undefined); // true
isUndefined(null); // false
isWrappable(value)
Returns true
if value
isIterable or value
isNil (and false
otherwise). When isWrappable(value)
, it is safe to pass value to wrap (and any other method which exepects a Wrappable). Type-safe in typescript.
isWrappable([]); // true
isWrappable(undefined); // true
isWrappable(null); // true
isWrappable({}); // false
notAsyncIterable(value)
Returns false
if value
has a Symbol.asyncIterator
property and true
otherwise. Type-safe in typescript.
notAsyncIterable((async function* () {})()); // false
notAsyncIterable((function* () {})()); // true
notAsyncIterable([]); // true
notAsyncIterable({}); // true
notAsyncIterable(undefined); // true
notAsyncIterable(null); // true
notAsyncLoopable(value)
Returns false
if value
has a Symbol.asyncIterator
or Symbol.iterator
property and true
otherwise. When notAsyncLoopable(value)
, using value as the subject of a for await..of
loop will throw an error.
notAsyncLoopable((async function* () {})()); // false
notAsyncLoopable((function* () {})()); // false
notAsyncLoopable([]); // false
notAsyncLoopable(undefined); // true
notAsyncLoopable(null); // true
notAsyncLoopable({}); // true
notAsyncWrappable(value)
Returns false
if value
isAsyncIterable, isIterable, or isNil (and true
otherwise). When notAsyncWrappable(value)
, passing value
to asyncWrap (or any other method which expects a AsyncWrappable) will throw an error.
notAsyncWrappable([]); // false
notAsyncWrappable(undefined); // false
notAsyncWrappable(null); // false
notAsyncWrappable((function* () {})()); // false
notAsyncWrappable((async function* () {})()); // false
notAsyncWrappable({}); // true
notAsyncWrappable(4); // true
notIterable(value)
Returns false
if value
is iterable (has a Symbol.iterator
property) and true
otherwise. For more details see the method's inverse: isIterable. Type-safe in typescript.
notIterable({}); // true
notIterable(undefined); // true
notIterable(null); // true
notIterable((function* () {})()); // false
notIterable([]); // false
See notIterable. For sync iterables these methods share the same implementation.
notNil(value)
Returns false
if value
is null
or undefined
and true
otherwise. Type-safe in typescript.
notNil(0); // true
notNil(undefined); // false
notNil(null); // false
notNull(value)
Returns false
if value
is null
and true
otherwise. Type-safe in typescript.
notNull(undefined); // true
notNull(null); // false
notUndefined(value)
Returns false
if value
is undefined
and true
otherwise. Implemented using typeof
. Type-safe in typescript.
notUndefined(null); // true
notUndefined(undefined); // false
notWrappable(value)
Returns false
if value
isIterable or value
isNil (and true
otherwise). When notWrappable(value)
, passing value
to wrap (or any other method which expects a Wrappable) will throw an error.
notWrappable([]); // false
notWrappable(undefined); // false
notWrappable(null); // false
notWrappable({}); // true
notWrappable(4); // true
apply(fn, ?args)
__apply(fn, ?args)
apply
is a convenience method. Its implementation is:
(fn, args = []) => fn(...args);
apply
has three main differences from Function.prototype.apply
. It does not take a thisArg
, the args to apply may be specified as an iterable, and if you do not pass the args
iterable, the result is a partial application, not a no-args call to fn
.
arrayFirst(array)
return array ? array[0] : undefined
arrayFirstOr(whenEmpty, array)
return array && array.length ? array[0] : whenEmpty
arrayLast(array)
return array ? array[array.length - 1] : undefined
arrayLastOr(whenEmpty, array)
return array && array.length ? array[array.length - 1] : whenEmpty
arrayReverse(source)
__arrayReverse(source)
Yields the elements from source
in reverse order. source
must be an array, string, null
, or undefined
.
call(fn, ...args)
__call(fn, ...args)
call
is a convenience method. Its implementation is:
(fn, ...args) => fn(...args);
call
has only one difference from Function.prototype.call
, which is that it does not take a thisArg
.
compose(...fns)
Allows nested calls to be flattened out for improved readability. compose(a, b, c)
is equivalent to a(b(c))
, where a
, b
, and c
, are functions. compose
is usually combined with curryied forms of other methods so that the source
(or iterable
) argument is passed between the composed methods.
const filterMap = compose(
map((x) => x + 1),
filter((x) => x % 2 === 0),
);
filterMap([0, 1, 2, 3, 4]); // Iterable[1, 3, 5]
Note: If seems backwards to you that filter
appears below map
yet runs first, you're probably looking for pipe.
execPipe(initial, ...fns)
execPipe(inital, ...fns)
is sugar for pipe(...fns)(initial)
. See pipe
execPipe(
[0, 1, 2, 3, 4],
filter(x => x % 2 === 0)
map(x => x + 1),
); // Iterable[1, 3, 5]
getSize(sequence)
Returns the size of sequence
as determined by accessing length
if Array.isArray(sequence)
, or sequence.size
otherwise. The size of null
or undefined
is 0
. Throws an error if a numeric size cannot be found in this way. If you have an iterable with no cached size you should instead use size.
getSize([1, 2, 3]); // 3
getSize(new Map([1, 2, 3])); // 3
getSize(null); // 0
pipe(...fns)
Allows nested calls to be flattened out for improved readability. pipe(a, b, c)
is equivalent to c(b(a))
, where a
, b
, and c
, are functions. pipe
is usually combined with curryied forms of other methods so that the source
(or iterable
) argument is passed between the composed methods.
const filterMap = pipe(
filter((x) => x % 2 === 0),
map((x) => x + 1),
);
filterMap([0, 1, 2, 3, 4]); // Iterable[1, 3, 5]
Note: pipe
is equivalent to compose but with inverted order of operations.
when(condition, value)
when
is a helper for use with the es6 spread syntax (the ...
operator). When condition
is truthy its result is value
(value()
if value
is callable). When condition is falsy its result is an empty iterable object. This is useful to avoid an unnecessarily difficult to read pattern that often causes code formatters (prettier, specifically) to emit an undesireable number of lines:
const always = true;
const sometimes = Math.random() > 0.5;
const arr = [always, ...(sometimes ? [sometimes] : [])]; // [true, true] OR [true]
Instead, you can use the when method like so:
const whenArr = [
always,
...when(sometimes, [sometimes]),
...when(sometimes, null), // nothing to spread? no problem
]; // [true, true] OR [true]
The pattern works equally well with objects.
const whenObj = {
always,
...when(sometimes, { sometimes }),
...when(sometimes, null),
}; // { always: true } OR { always: true, somtimes: true }
If value
is a function it will only be evaluated when expression
is true, simulating the short-circuit behavior of ternary expressions. This can help you avoid doing unnecessary expensive work:
const whenArr = [
always,
...when(sometimes, () => expensiveExpression),
]; // [] OR [...expensiveExpression]
forkerate(source)
__forkerate(source)
Turns source
into a forkerator (often shortened to forkr
). At each step the user may call forkr.fork()
to create a new iterable which yields values from source
starting with forkr.value
. Consumed values are cached efficiently until every active fork has read them.
interface Forkerator<T> extends Peekerator<T> {
/**
* Calls `next()` `n` times on the underlying iterator and stores the value in `current`.
* Returns `this` for chaining.
*/
advance(n: number): this;
/**
* The forked iterator yields values starting from the current position.
*/
fork(): SingletonIterableIterator;
}
forkerate()
is expected to be particularly useful for writing streaming parsers.
import { forkerate, startsWithSeq } from '@iter-tools';
export function* stripComments(source) {
const forkr = forkerate(source);
while (true) {
const isComment = startsWithSeq('//', forkr.fork());
while (forkr.value !== '\n') {
if (!isComment) yield forkr.value;
forkr.advance();
if (forkr.done) return;
}
if (!isComment) yield '\n';
forkr.advance();
}
}
str(stripComments('// comment\ncode')); // 'code'
asyncForkerate(source)
__asyncForkerate(source)
See forkerate
interleave(strategy, options, ...sources)
interleave(strategy, ...sources)
__interleave(sources, strategy, ?options)
Facilitates the creation of new strategies for interleaving values from multiple iterables. It does this by decorating the strategy
generator, which is to say providing it with arguments and yielding its values. The responsibilities of the wrapping code are to forward any provided options
, decorate the source iterables with peekerators, manage the special all
peekerator, and to call return()
on any incomplete peekerators. The all
peekerator provides all.done
to indicate whether interleaving is complete, and all.value
: a reference to the first peekerator which is not done. The all
peekerator cannot be advanced.
Both collate and roundRobin are implemented using interleave
, and it is expected that most use cases will be served by one or the other. Their implementations also serve as useful examples.
function* alternatingStrategy(
options,
all,
...peekerators
) {
const { count = 1 } = options;
while (!all.done) {
for (const peekr of peekerators) {
for (let i = 0; i < count; i++) {
if (!peekr.done) {
yield peekr.value;
peekr.advance();
}
}
}
}
}
const alternatingInterleave = interleave(
alternatingStrategy,
);
const a = [1, 2, 5, 6];
const b = [3, 4, 7];
alternatingInterleave({ count: 2 }, a, b); // [1, 2, 3, 4, 5, 6, 7]
Note: This method has only cursory Typecript support because Typescript lacks the power to describe it. Instead you should include your own type definitions. The example code with typedefs might look like this:
function* alternatingStrategy<T>(
options: { count: number },
all: Peekerator<Peekerator<T>>,
...peekerators: Array<Peekerator<T>>
) {
// implementation is unchanged, but now type-safe
}
function alternatingInterleave<T>(
count: number,
...sources: Array<Iterable<T>>
): IterableIterator<T> {
return interleave(alternatingStrategy, { count }, ...sources);
});
A final note: if you are creating a strategy which takes no options, it would be wise to bind an empty options object to avoid the confusion that iter-tools' partial application rules could cause.
const myInterleave = interleave(myStrategy, {});
asyncInterleave(strategy, options, ...sources)
asyncInterleave(strategy, ...sources)
__asyncInterleave(sources, strategy, ?options)
See interleave
peekerate(source)
__peekerate(source)
Turns source
into a peekerator (often shortened to peekr
), which is conceptually equivalent to an iterator but is often easier to work with. Peekerators always have a current
step (of the shape { done, value }
) from the source iterator stored as peekr.current
. This stateful API is useful since it allows you to see the current step without consuming it. This way you can choose to do nothing so that another part of your code will have the chance to act on the value instead. This is highly value in a number of common scenarios. A simple and common one is reacting when an iterable is empty:
function printValues(values) {
const peekr = peekerate(values);
return peekr.done
? 'none'
: stringFrom(interposeSeq(', ', peekr.asIterator()));
}
printValues([]); // 'none'
printValues([1, 2, 3]); // '1, 2, 3'
Here is the full interface that the peekr
object conforms to:
interface Peekerator<T> {
/**
* Calls `next()` on the underlying iterator and stores the value in `current`.
* Returns `this` for chaining.
*/
advance(): this;
/**
* Calls `return()` on the underlying iterator.
* Returns `this` for chaining.
*/
return(): this;
/**
* Returns an iterable iterator which starts at the `current` step.
* This means that equal(source, peekerate(source).asIterator()) is true
*/
asIterator(): $IterableIterator<T>;
/**
* Returns the step object that was return by `next()`.
* The actual typedefs define a tagged union for safety.
*/
readonly current: { done: boolean; value: T | undefined };
/**
* A convenince getter for `current.done`
*/
readonly done: boolean;
/**
* A convenince getter for `current.value`
*/
readonly value: T;
/**
* The index of the step stored in `current`
*/
readonly index: number;
}
Typescript note: The type of peekr
(and peekr.current
) will be refined when you use the value of peekr.done
as a condition. This helps you avoid spurious errors about peekr.value
potentially being undefined
, but it gives rise to another problem: Tyescript doesn't understand that peekr.advance()
makes its previous refinements invalid. Therefore you must help typescript understand this by writing peekr = peekr.advance()
. If you do not do this you may discover that Typescript fails to catch some errors, or you may just give strange-looking errors about the type of peekr
being never
. This happens because typescript has refined the type of peekr.done
twice, once to true
and once to false
. The type of true & false
is never
in Typescript.
asyncPeekerate(source)
__asyncPeekerate(source)
See peekerate
Note: Returns a promise of a peekerator, which is necessary for the first value to be fetched.
function asyncPrintValues(values) {
const peekr = await asyncPeekerate(values);
return peekr.done
? 'none'
: stringFromAsync(
asyncInterposeSeq(', ', peekr.asIterator()),
);
}
asyncPrintValues(asyncWrap([])); // 'none'
asyncPrintValues(asyncWrap([1, 2, 3])); // '1, 2, 3'
spliterate(strategy, options, source)
spliterate(strategy, source)
__spliterate(source, strategy, ?options)
Facilitates the creation of methods which split a source
iterable into multiple parts. The strategy
generator yield a flat output containing values from source
as well as special split
sentinel values. spliterate
decorates the values yielded from strategy()
. Each instance of the split
sentinel will yield a new part. Thus for a strategy
which yields split
n
times, n + 1
parts will be yielded.
Other methods in the split* family (e.g. splitOn, splitWhen, and bisect) are implemented using spliterate
under the hood. It is expected that most use cases will be served by one of these existing methods. Their implementations also serve as useful examples.
Here is a slightly simplified implementation of batch:
function* batchStrategy(split { size }, source) {
for (const [value, i] of enumerate(source)) {
if (i % size === 0) yield split;
yield value;
}
}
const batch = spliterate(batchStrategy);
const iterable = [0, 'a', 1, 'b', 2, 'c'];
for (const [idx, letter] of batch({ size: 2 }, iterable)) {
log(idx, letter);
}
// 0 a
// 1 b
// 2 c
Note: This method has only cursory Typecript support because Typescript lacks the power to describe it. Instead you should include your own type definitions. The example code with typedefs might look like this:
function* batchStrategy<T>(
split: symbol,
options: { size: number },
source: Iterable<T>,
) {
// implementation is unchanged
}
function batch<T>(
size: number,
source: Iterable<T>,
): IterableIterator<IterableIterator<T>> {
return spliterate(batchStrategy, { size }, source);
}
A final note: if you are creating a strategy which takes no options, it would be wise to bind an empty options object to avoid the confusion that iter-tools' partial application rules could cause.
const mySpliterate = spliterate(myStrategy, {});
asyncSpliterate(strategy, options, source)
asyncSpliterate(strategy, source)
__asyncSpliterate(source, strategy, ?options)
See spliterate
spliterateGrouped(strategy, options, source)
spliterateGrouped(strategy, source)
__spliterateGrouped(source, strategy, ?options)
Facilitates the creation of methods which split a source
iterable into multiple keyed groups. The strategy
generator yield a flat output containing values from source
as well as special split
sentinel values. spliterate
decorates the values yielded from strategy()
. Each instance of the split
sentinel starts a new group. The value immediately following a split
is the key for the group. This means that a strategy
which yields split
n
times, n
groups will be yielded.
splitGroupsBy is implemented using spliterateGrouped
under the hood. It is expected that most use cases will be served by using that method instead.
Included as an example is a lightly edited version of the implementation of splitGroupsBy
. It is expected that in the vast majority of circumstances it will be correct to use the actual splitGroupsBy method and not this one.
function* groupingSpliterator(split, { getKey }, source) {
const peekr = peekerate(source);
let key = Symbol();
while (!peekr.done) {
const lastKey = key;
key = getKey(peekr.value);
if (lastKey !== key) {
yield split;
yield key;
}
yield peekr.value;
peekr.advance();
}
}
function splitGroupsBy(source, getKey) {
return spliterateGrouped({ getKey }, source);
}
Note: This method has only cursory Typecript support because Typescript lacks the power to describe it. Instead you should include your own type definitions. The example code with typedefs might look like this:
function* groupingSpliterator<T>(
split: symbol,
options: { getKey: Function },
source: Iterable<T>,
) {
// implementation is unchanged
}
function splitGroupsBy<K, T>(
getKey: (value: T) => K,
source: Iterable<T>,
): IterableIterator<[K, IterableIterator<T>]> {
return spliterateGrouped(
groupingSpliterator,
{ getKey },
source,
);
}
A final note: if you are creating a strategy which takes no options, it would be wise to bind an empty options object to avoid the confusion that iter-tools' partial application rules could cause.
const mySpliterate = spliterate(myStrategy, {});
asyncSpliterateGrouped(strategy, options, source)
asyncSpliterateGrouped(strategy, source)
__asyncSpliterateGrouped(source, strategy, ?options)