Skip to content

Iterables

The Module Iterables module contains functions working with iterables and generators. These functions work with synchronous and asynchronous input.

Filtering/finding

filter yields values from iterator that match a predicate function. A predicate function is takes an input value and returns true or false. filter essentially allows iterating over a sub-set of the input data:

for (const v of Iterables.filter(data, e=> e.startsWith(`a`)) {
// All items from `data` that start with letter `a`
}

dropWhile is the opposite of filter, yielding values that do not match the predicate.

for (const v of Iterables.dropWhile(data, e=> e.startsWith(`a`)) {
// All items from `data` that do not start with letter `a`
}

find Returns first value from iterator that matches predicate.

const v = Iterables.find(data, e=> e.startsWith(`a`));
// v will be first item in `data` that starts with letter `a` or _undefined_ if not found

some Returns true if a value matching a predicate is found.

const v = Iterables.some(data, e=> e.startsWith(`a`));
// v will be true if `data` contains an item that starts with `a`

reduce is useful when you need to collapse the values of an iterable down to a single value. For example, you may have an iterable of numbers and you want a single result, the sum of all values.

reduce’s first parameter is the source iterable, then a reducer function, and then a start value.

const reducer = (accumulated, current) => accumulator + current;
const v = Iterables.reduce(data, reducer, 0);

The reducer function takes two parameters, the accumulated value and the current value at this position in the iteration.

In the above example, we sum the currently-accumulated value with the current value, and use a starting value of 0.

Re-shaping

map yields values transformed through a function.

for (const v of Iterables.map(data, v => v.toUpperCase())) {
// Uppercased versions of every value in `data`
}

chunks breaks up an iterable into certain sized ‘chunk’ of values. Each chunk is given as an array.

const data = [1,2,3,4,5,6,7,8,9,10];
for (const chunk of Iterables.chunk(data, 3)) {
// [1,2,3]
// [4,5,6]
// [7,8,9]
// [10]
}

concat combines several iterables, yielding each result from one iterable, then the next and so on. It’s a bit like combining arrays with: const joined = [...array1, ....array2]

for (const v of Iterables.concat(iter1, iter2)) {
// ..each value of iter1,
// ..each value of iter2, etc
}

flatten returns values from an iterable, but if the value is an array, returns values from within the array.

Iterables.flatten([1, [2, 3], [[4]]]);
// Yields: [1, 2, 3, [4]];
// Flattening is only one-level deep, so [4] is returned still.

slice yields a subset of an iterable from a specified start number of steps to an end number of steps, or the completion of the iterable.

// Eg yields five items from step 10 of `data`
for (const v of Iterables.slice(data, 10, 15)) {
}

Conversions

toArray : Copy contents of iterable to an array. While this can be done with in-built JS functions, ixfx’s toArray allows you to set limits for the number of items, or duration. This is useful when dealing with infinite generators.

fromArray : Yields values from an array with a delay between each.

fromIterable : Yields values from an input iterable over time.

fromEvent : Yields values from an event (eg a pointer move)

fromFunction : Yields values from a function.

fromFunctionAwaited : Yields values from an asynchronous function.

Synchronising

combineLatestToArray : Values from several iterables are emitted as a batch whenever one of the iterables emits a value. If an iterable has not yet emitted a value, undefined is used.

for await (const v of Iterables.combineLatestToArray([data1,data2,data3])) {
// v will be [valueFromData1, valueFromData2, valueFromData3]
// where index of v matches list of sources
}

The output array’s indexes match the index of the sources when calling combineLatestToArray. Eg in the above example the value from data1 will be in the first position of array v.

zip combines items at same position. This is useful for matching items between iterables. Only use this when you are sure that the order of iterables makes sense between iterables, eg fourth iteration step in all the input iterables connects data logically.

Iterables.zip( [1, 2, 3], [4, 5, 6], [7, 8, 9] );
// Yields: [ [1, 4, 7], [2, 5, 8], [3, 6, 9] ]

Comparisons

every Returns true if predicate is true for every item in iterable

if (Iterables.every(data, v => v.startsWith(`a`))) {
// Every value in `data` starts with `a`
}

equals Returns true if values in two iterables are the same at the same location, judged by a provided equality function.

// Assuming data1 & 2 have objects with property 'name'...
if (Iterables.equals(data1, data2, (a,b) => a.name === b.name)) {
// data1 & 2 have objects with same name at same step of iteration.
}

Order matters with equals. If two iterables have the same data but at different locations, it will return false.

max /min Yields the currently highest max/min value from an iterable, based on a given ranking function.

unique Return a set of unique items, compared by reference. Ie, that they are the same object, rather than same value.

for (const v of Iterables.unique(data1, data2)) {
// Will enumerate unique values in the combination of data1 & 2.
}

For working immutable values, use uniqueByValue which compares by value.

Importing

Importing Module Iterables

// Whole module
import * as Iterables from "ixfx/iterables.js"
// Single function
import { concat } from "ixfx/iterables.js"
// One of several modules
import { Iterables, Modulation, Flow, Data } from "ixfx/bundle.js"
// And within your HTML's <HEAD> </HEAD> block:
// <script type="importmap">{ "imports": { "ixfx/": "/ixfx/" } } </script>