Skip to content

Latest commit

 

History

History
816 lines (607 loc) · 22.4 KB

api.md

File metadata and controls

816 lines (607 loc) · 22.4 KB

API

Table of Contents

Starting a chain

chainFrom(iterable)

Starts a chain. Any number of transformation methods may be added, after which a termination method should be called to produce a result. No computation is done until a termination method is called.

The argument may be any iterable, such as an array, a string, or an ES6 Set. This is back-compatible with older browsers which did not implement the Iterable interface for arrays and strings.

transducerBuilder()

Starts a chain for constructing a new transducer. Any number of transformation methods may be added, after which .build() should be called to produce a transducer.

Transformation methods

Any number of these methods may be called on a chain to add transformations in sequence.

.dedupe()

Removes elements that are equal to the proceeding element (using === for equality). For example:

chainFrom([1, 2, 2, 3, 3, 3])
    .dedupe()
    .toArray(); // -> [1, 2, 3]

.drop(n)

Skips the first n elements. If there are fewer than n elements, then skip all of them. If n is negative, then leave the elements unchanged (same as 0).

For example:

chainFrom([1, 2, 3, 4, 5])
    .drop(3)
    .toArray(); // -> [4, 5]

.dropWhile(pred)

Skips elements as long as the predicate pred holds. For example:

chainFrom([1, 2, 3, 4, 5])
    .dropWhile(n => n < 3)
    .toArray(); // -> [3, 4, 5]

.filter(pred)

Keeps only the elements matching the predicate pred. For example:

chainFrom([1, 2, 3, 4])
    .filter(x => x % 2 === 1)
    .toArray(); // -> [1, 3]

.flatMap(f)

For f a function which maps each element to an iterable, applies f to each element and concatenates the results. For example:

const authors = [
    { name: "cbrontë", books: ["Jane Eyre", "Shirley"] },
    { name: "mshelley", books: ["Frankenstein"] },
];

chainFrom(authors)
    .flatMap(author => author.books)
    .toArray();
// -> ["Jane Eyre", "Shirley", "Frankenstein"]

.flatten()

For a chain of iterables, concatenates the contents of each iterable. For example:

chainFrom([[0, 1], [2], [], [3, 4]])
    .flatten()
    .toArray(); // [0, 1, 2, 3, 4]

.interpose(separator)

Inserts separator between each pair of elements. For example:

chainFrom([1, 2, 3, 4, 5])
    .interpose(0)
    .toArray();
// -> [1, 0, 2, 0, 3, 0, 4, 0, 5]

.map(f)

Transforms each element by applying f to it. For example:

chainFrom([1, 2, 3])
    .map(x => x * 2)
    .toArray(); // -> [2, 4, 6]

.mapIndexed(f)

Transforms each element by applying f to the element and the current index in the sequence. For example:

chainFrom(["a", "b", "c"])
    .mapIndexed((x, i) => [x, i])
    .toArray(); // -> [["a", 0], ["b", 1], ["c", 2]]

.partitionAll(n)

Groups elements into arrays of n elements. If the number of elements does not divide perfectly by n, the last array will have fewer than n elements. Throws if n is nonpositive. For example:

chainFrom([1, 2, 3, 4, 5])
    .partitionAll(2)
    .toArray();
// -> [[1, 2], [3, 4], [5]]

.partitionBy(f)

Groups consecutive elements for which f returns the same value (as determined by ===) into arrays. For example:

chainFrom(["a", "ab", "bc", "c", "cd", "cde"])
    .partitionBy(s => s[0])
    .toArray();
// -> [["a", "ab"], ["bc"], ["c", "cd", "cde"]]

.remove(pred)

Like filter(), but removes the elements matching pred instead. For example:

chainFrom([1, 2, 3, 4])
    .remove(x => x % 2 === 1)
    .toArray(); // -> [2, 4]

.removeAbsent()

Removes null and undefined elements (but not other falsy values). For example:

chainFrom([0, 1, null, 2, undefined, 3])
    .removeAbsent()
    .toArray(); // -> [0, 1, 2, 3]

.take(n)

Takes the first n elements and drops the rest. An essential operation for efficiency, because it stops computations from occurring on more elements of the input than needed to produce n results. If there are less than n elements, then leave all of them unchanged. If n is negative, then take none of them (same as 0). For example:

chainFrom([1, 2, 3, 4, 5])
    .take(3)
    .toArray(); // -> [1, 2, 3]

.takeNth(n)

Takes every nth element, starting from the first one. In other words, it takes the elements whose indices are multiples of n. Throws if n is nonpositive. For example:

chainFrom([1, 2, 3, 4, 5, 6])
    .takeNth(2)
    .toArray(); // [1, 3, 5]

.takeWhile(pred)

Takes elements as long as the predicate pred holds, then drops the rest. Like take(), stops unnecessary computations on elements after pred fails. For example:

chainFrom([1, 2, 3, 4, 5])
    .takeWhile(n => n < 3)
    .toArray(); // -> [1, 2]

.compose(transducer)

Add an arbitrary transducer to the chain. transducer should be a function which implements the transducer protocol, meaning it is a function which takes a Transformer and returns another Transformer. This is the most general transformation, and it is used by this library internally to implement all the others. For example usage, see the Using custom transducers section of the main readme.

Ending a chain

The following methods terminate a chain started with chainFrom, performing the calculations and producing a result.

.average()

For a chain of numbers, return their average, or null if there are no elements. For example:

chainFrom(["a", "bb", "ccc"])
    .map(s => s.length)
    .average(); // -> 2

.count()

Returns the number of elements. For example:

chainFrom([1, 2, 3, 4, 5])
    .filter(x => x % 2 === 1)
    .count(); // -> 3

.every(pred)

Returns true if all elements satisfy the predicate pred, or false otherwise. Short-circuits computation once a failure is found. Note that this is equivalent to .remove(pred).isEmpty().

Example:

chainFrom([1, 2, 3, 4, 5])
    .map(n => 10 * n)
    .every(n => n > 3); // -> true

chainFrom([1, 2, 3, 4, 5])
    .map(n => 10 * n)
    .every(n => n < 30); // -> false

.find(pred)

Returns the first element of the result which satisfies the predicate pred, or null if no such element exists. Short-circuits computation once a match is found. Note that this is equivalent to .filter(pred).first().

Example:

chainFrom([1, 2, 3, 4, 5])
    .map(x => x * 10)
    .find(x => x % 6 === 0); // -> 30

.first()

Returns the first element of the result, or null if there are no elements. Short-circuits computation after reading the first element.

Example:

chainFrom([1, 2, 3, 4, 5])
    .map(x => x * 10)
    .first(); // -> 10

.forEach(f)

Calls f on each element of the result, presumably for side-effects. For example:

chainFrom([1, 2, 3, 4, 5])
    .map(x => x * 10)
    .forEach(x => console.log(x));
// Prints 10, 20, 30, 40, 50

.isEmpty()

Returns true if there are any elements, else false. Short-circuits computation after reading one element. For example:

chainFrom([1, 2, 3, 4, 5])
    .filter(n => n > 10)
    .isEmpty(); // -> true

chainFrom([1, 2, 3, 4, 5])
    .filter(n => n % 2 === 0)
    .isEmpty(); // -> false

.joinToString(separator)

Returns a string obtained by concatenating the elements together as strings with the separator between them. For example:

chainFrom([1, 2, 3, 4, 5])
    .filter(n => n % 2 === 1)
    .joinToString(" -> "); // -> "1 -> 3 -> 5"

Not called toString() in order to avoid clashing with the Object prototype method.

.max(comparator?)

Returns the maximum element, according to the comparator. If the elements are numbers, then this may be called without providing a comparator, in which case the natural comparator is used. Returns null if there are no elements.

Example:

chainFrom(["a", "bb", "ccc"])
    .map(s => s.length)
    .max(); // -> 3

.min(comparator?)

Returns the minimum element, according to the comparator. If the elements are numbers, then this may be called without providing a comparator, in which case the natural comparator is used. Returns null if there are no elements.

Example:

chainFrom(["a", "bb", "ccc"])
    .map(s => s.length)
    .min()); // -> 1

.some(pred)

Returns true if any element satisfies the predicate pred, or false otherwise. Short-circuits computation once a match is found. Note that this is equivalent to .filter(pred).isEmpty() === false.

Example:

chainFrom([1, 2, 3, 4, 5])
    .map(n => 10 * n)
    .some(n => n === 30); // -> true

chainFrom([1, 2, 3, 4, 5])
    .map(n => 10 * n)
    .some(n => n === 1); // -> false

.sum()

For a chain of numbers, return their sum. If the input is empty, return 0. For example:

chainFrom(["a", "bb", "ccc"])
    .map(s => s.length)
    .sum(); // -> 6

.toArray()

Returns an array of the results. See any of the above examples.

.toMap(getKey, getValue)

Returns an ES6 Map, each of whose key-value pairs is generated by calling the provided funtions on each element in the result. If multiple elements produce the same key, then the value produced by the latest element will override the earlier ones. Similar to .toObject(), except that the getKey function is permitted to return any type of value, not just a string.

This function assumes that Map is present in your environment. If you call this function, you are responsible for providing a polyfill if your environment does not natively support Map.

Example:

const authors = [
    { name: "cbrontë", books: ["Jane Eyre", "Shirley"] },
    { name: "mshelley", books: ["Frankenstein"] },
];

chainFrom(authors).toMap(
    a => a.id,
    a => a.books.length,
);
// -> Map{ "cbrontë" -> 2, "mshelley" -> 4 }

.toMapGroupBy(getKey, transformer?)

Produces an ES6 Map by grouping together all elements for which getKey returns the same value under that key. By default, each key corresponds to an array of the elements which produced that key.

Optionally, a transformer may be passed as the second argument to configure the reduction behavior of the values. All chain-ending methods in this section other than .toIterator() have a standalone variant which produces a transformer (for details, see the section Tree shakeable API), which opens many possibilities. Some advanced examples are shown below.

Similar to .toObjectGroupBy(), except that the getKey function is permitted to return any type of value, not just a string.

This function assumes that Map is present in your environment. If you call this function, you are responsible for providing a polyfill if your environment does not natively support Map.

Examples:

chainFrom(["a", "b", "aa", "aaa", "bc"]).toMapGroupBy(s => s[0]);
// -> Map{ "a" -> ["a", "aa", "aaa"], "b" -> ["b", "bc"] }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toMapGroupBy(s => s[0], count());
// -> Map{ "a" -> 3, "b" -> 2 }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toMapGroupBy(
    s => s[0],
    some(s => s.length === 3),
);
// -> Map{ "a" -> true, "b" -> false }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toMapGroupBy(
    s => s[0],
    map((s: string) => s.length)(toAverage()),
);
// -> Map{ "a" -> 2, "b" -> 1.5 }

.toObject(getKey, getValue)

Returns an object, each of whose key-value pairs is generated by calling the provided functions on each element in the result. If multiple elements produce the same key, then the value produced by the latest element will override the earlier ones. Similar to .toMap(), except that the getKey function is required to return a string.

Example:

const authors = [
    { name: "cbrontë", books: ["Jane Eyre", "Shirley"] },
    { name: "mshelley", books: ["Frankenstein"] },
];

chainFrom(authors).toObject(
    a => a.id,
    a => a.books.length,
);
// -> { cbrontë: 2, mshelley: 1 }

.toObjectGroupBy(getKey, transformer?)

Produces an object by grouping together all elements for which getKey returns the same value under that key. By default, each key corresponds to an array of the elements which produced that key.

Optionally, a transformer may be passed as the second argument to configure the reduction behavior of the values. All chain-ending methods in this section other than .toIterator() have a standalone variant which produces a transformer (for details, see the section Tree-shakeable API), which opens many possibilities. Some advanced examples are shown below.

Similar to .toMapGroupBy(), except that the getKey function is required to return a string.

Examples:

chainFrom(["a", "b", "aa", "aaa", "bc"]).toObjectGroupBy(s => s[0]);
// -> { a: ["a", "aa", "aaa"], b: ["b", "bc"] }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toObjectGroupBy(s => s[0], count());
// -> { a: 3, b: 2 }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toObjectGroupBy(
    s => s[0],
    some(s => s.length === 3),
);
// -> { a: true, b: false }

chainFrom(["a", "b", "aa", "aaa", "bc"]).toMapGroupBy(
    s => s[0],
    map((s: string) => s.length)(toAverage()),
);
// -> { a: 2, b: 1.5 }

.toSet()

Returns an ES6 Set of the results.

This function assumes that Set is present in your environment. If you call this function, you are responsible for providing a polyfill if your environment does not natively support Set.

.toIterator()

Returns an iterator. Elements of the input iterator are not read until this iterator is read, and then only as many as needed to compute the number of results requested. This is the primary way of reading results lazily.

Example:

const iterator = chainFrom([1, 2, 3, 4, 5])
    .map(x => x * 10)
    .toIterator();
console.log(iterator.next().value()); // Prints 10
// So far, the map function has only been called once.

.reduce(reducer, intialValue?)

Reduces the elements according to the reducer, and returns the result. reducer may be either a plain function of the form (acc, x) => acc or a transformer as defined by the transformer protocol. This is the most general way to terminate a chain, and all the others (except for toIterator) are implemented using it.

Example of using a plain function reducer:

chainFrom([1, 2, 3, 4, 5])
    .map(x => x * 10)
    .reduce((acc, x) => acc + x, 0); // -> 150

A handful of pre-made transformers are provided by this library to be used with reduce(). They are described in the next section.

Iterables

Helper functions for producing iterables, often useful for starting chains.

cycle(iterable)

Returns an iterable which repeats the elements of the input iterable indefinitely. If the input iterable is empty, then produces an empty iterable. For example:

chainFrom(cycle(["a", "b", "c"]))
    .take(7)
    .toArray(); // -> ["a", "b", "c", "a", "b", "c", "a"]

chainFrom(cycle([])).toArray(); // -> []

iterate(initialValue, f)

Returns an iterable which emits initialValue, f(initialValue), f(f(initialValue)), and so on. For example:

chainFrom(iterate(1, x => 2 * x))
    .take(5)
    .toArray(); // -> [1, 2, 4, 8, 16]

repeat(value, count?)

Returns an iterable which emits value repeatedly. If count is provided, then emits value that many times. If count is omitted, then emits value indefinitely. Throws if count is negative. For example:

chainFrom(repeat("x"))
    .take(3)
    .toArray(); // -> ["x", "x", "x"]

chainFrom(repeat("x", 3)).toArray(); // -> ["x", "x", "x"]

range(start?, end, ste?)

Returns an iterable which outputs values from start inclusive to end exclusive, incrementing by step each time. start and step may be omitted, and default to 0 and 1 respectively.

If step is positive, then outputs values incrementing upwards from start until the last value less than end. If step is negative, then outputs values incrementing downwards from start until the last value greater than end.

A start greater than or equal to end for positive step, or a start less than or equal to end for a negative step, is permitted, and produces an empty iterator.

Throws an error if step is zero.

Example:

chainFrom(range(3))
    .map(i => "String #" + i)
    .toArray(); // -> ["String #0", "String #1", "String #2"]

chainFrom(range(10, 15)).toArray(); // -> [10, 11, 12, 13, 14]

chainFrom(range(10, 15, 2)).toArray(); // -> [10, 12, 14]

chainFrom(range(15, 10, -2)).toArray(); // -> [15, 13, 11]

Values are produced lazily, so for example the following will return quickly and not use up all your memory:

chainFrom(range(1000000000000))
    .take(3)
    .toArray(); // -> [0, 1, 2]

Utility functions

isReduced(result)

Returns true if result is a reduced value as described by the transducer protocol.

reduced(result)

Returns a reduced value of result, as described by the transducer protocol. Can be returned by a reducer or a transformer to short-circuit computation.

Tree shakeable API

As discussed in the readme section Bundle Size and Tree Shaking, Transducist also provides standalone functions with the same behavior as the chain, for the purposes of reducing bundle size. In particular, all chain methods (except toIterator()) have a standalone function of the same name.

For those familiar with the transducer protocol, the standalone functions corresponding to the transform methods (e.g. map(), take()) each produce a transducer, while the standalone functions corresponding to the end of the chain (e.g. toArray(), count()) each produce a transformer.

In addition to the standalone functions whose names match the methods listed above, the tree-shakeable API is completed by the functions below.

compose(f1, f2, ...)

Composes any number of transducers together to produce a new transducer. This is actually just ordinary function composition, although its TypeScript typings are specialized for transducers in particular.

transduce(iterable, transducer, transformer)

(Or: transduce(iterable, transducer, reducer, initialValue))

Starting from the iterable, applies the transformations specified by the transducer and then uses the transformer to construct a final result.

Rather than providing a transformer as the third argument, this may also be called by passing an ordinary reducer function and an initial value as the final two arguments, where a reducer is a plain function of the form (acc, x) => acc.

Example:

import { compose, filter, map, toArray, transduce } from "transducist";

transduce(
    [1, 2, 3, 4, 5],
    compose(
        filter(x => x > 2),
        map(x => 2 * x),
    ),
    toArray(),
); // -> [6, 8, 10]

which is equivalent to the chained version:

import { chainFrom } from "transducist";

chainFrom([1, 2, 3, 4, 5])
    .filter(x => x > 2)
    .map(x => 2 * x)
    .toArray(); // -> [6, 8, 10]

lazyTransduce(iterable, transducer)

Returns an iterator which lazily performs the transformations specified by the transducer. This is the standalone version of ending a chain with .toIterator(). That is, the following are equivalent:

import { compose, filter, map, lazyTransduce } from "transducist";

lazyTransduce(
    [1, 2, 3, 4, 5],
    compose(
        filter(x => x > 2),
        map(x => 2 * x),
    ),
);
import { chainFrom } from "transducist";

chainFrom([1, 2, 3, 4, 5])
    .filter(x => x > 2)
    .map(x => 2 * x)
    .toIterator();

Copyright © 2017 David Philipson