@thi.ng/transducers
Lightweight transducer implementations for ES6 / TypeScript (8KB minified, full lib).
The library provides 35 transducers and 15 reducers for composing data
transformation pipelines (more to come).
Please see the @thi.ng/iterators &
@thi.ng/csp partner modules for related
functionality, supplementing features of this library.
Installation
yarn add @thi.ng/transducers
Usage examples
import * as tx from "@thi.ng/transducers";
import { transduce } from "@thi.ng/transducers/transduce";
import { push } from "@thi.ng/transducers/rfn/push";
import { map } from "@thi.ng/transducers/xforms/map";
xform = tx.comp(
tx.filter(x => (x & 1) > 0),
tx.distinct(),
tx.map(x=> x * 3)
);
tx.transduce(xform, tx.push(), [1, 2, 3, 4, 5, 4, 3, 2, 1]);
tx.transduce(xform, tx.conj(), [1, 2, 3, 4, 5, 4, 3, 2, 1]);
for(let x of tx.iterator(xform, [1, 2, 3, 4, 5])) {
console.log(x);
}
tx.transduce(
tx.comp(
tx.partition(5, 1),
tx.map(x => tx.reduce(tx.mean(), x))
),
tx.push(),
[1, 2, 3, 3, 4, 5, 5, 6, 7, 8, 8, 9, 10]
);
tx.transduce(
tx.comp(
tx.inspect("orig"),
tx.map(x => x + 1),
tx.inspect("mapped"),
tx.filter(x => (x & 1) > 0)
),
tx.push(),
[1, 2, 3, 4]
);
tx.transduce(tx.map(x => x.toUpperCase()), tx.frequencies(), "hello world")
tx.reduce(tx.frequencies(), [1, 1, 1, 2, 3, 4, 4])
tx.transduce(
tx.comp(
tx.mapcat(x => x.split("\n")),
tx.map(x => x.split(",")),
tx.rename({ id: 0, name: 1, alias: 2, num: "length" })
),
tx.push(),
["100,typescript\n101,clojure,clj\n110,rust,rs"]
);
tx.transduce(
tx.comp(tx.flatten(), tx.take(7)),
tx.push(),
[1, [2, [3, 4, [5, 6, [7, 8], 9, [10]]]]]
)
xform = tx.comp(
tx.scan(tx.count),
tx.map(x => [...tx.iterator(tx.repeat(x), [x])]),
tx.scan(tx.pushCopy)
);
tx.transduce(xform, tx.push(), [1, 1, 1, 1]);
tx.transduce(tx.comp(tx.scan(tx.count), tx.scan(tx.pushCopy)), tx.push(), [1,1,1,1])
f = tx.step(tx.dedupe());
f(1);
f(2);
f(2);
f(3);
f(3);
f(3);
f(1);
API
Types
Apart from type aliases, the only real types defined are:
Reducer
Reducers are the core building blocks of transducers. Unlike other
implementations using OOP approaches, a Reducer
in this lib is a simple
3-element array of functions, each addressing a separate processing step.
Since v0.6.0 the bundled reducers are all wrapped in functions to provide a
uniform API (and some of them can be preconfigured). However, it's fine to
define stateless reducers as constant arrays.
interface Reducer<A, B> extends Array<any> {
[0]: () => A,
[1]: (acc: A) => A,
[2]: (acc: A, x: B) => A | Reduced<A>,
}
const push: Reducer<any[], any> = [
() => [],
(acc) => acc,
(acc, x) => (acc.push(x), acc),
];
Currently partition
, partitionBy
, streamSort
, streamShuffle
are the only operators making
use of the 1-arity completing function of their reducer.
Reduced
class Reduced<T> implements IDeref<T> {
protected value: T;
constructor(val: T);
deref(): T;
}
Simple type wrapper to identify early termination of a reducer. Does not modify
wrapped value by injecting magic properties. Instances can be created via
reduced(x)
and handled via these helper functions:
reduced(x: any): any
isReduced(x: any): boolean
ensureReduced(x: any): Reduced<any>
unreduced(x: any): any
Transducer
From Rich Hickey's original definition:
A transducer is a transformation from one reducing function to another
As shown in the examples above, transducers can be dynamically composed (using
comp()
) to form arbitrary data transformation pipelines without causing large
overheads for intermediate collections.
type Transducer<A, B> = (rfn: Reducer<any, B>) => Reducer<any, A>;
function map<A, B>(fn: (x: A) => B): Transducer<A, B> {
return (rfn: Reducer<any, B>) => {
return [
() => rfn[0](),
(acc) => rfn[1](acc),
(acc, x: A) => rfn[2](acc, fn(x))
];
};
}
function dedupe<T>(): Transducer<T, T> {
return (rfn: Reducer<any, T>) => {
let prev = {};
return [
() => rfn[0](),
(acc) => rfn[1](acc),
(acc, x) => {
acc = prev === x ? acc : rfn[2](acc, x);
prev = x;
return acc;
}
];
};
}
Transformations
comp(f1, f2, ...)
compR(rfn: Reducer<any, any>, fn: (acc, x) => any): Reducer<any, any>
iterator<A, B>(tx: Transducer<A, B>, xs: Iterable<A>): IterableIterator<B>
juxt(f1, f2, ...)
reduce<A, B>(rfn: Reducer<A, B>, acc: A, xs: Iterable<B>): A
transduce<A, B, C>(tx: Transducer<A, B>, rfn: Reducer<C, B>, acc: C, xs: Iterable<A>): C
Transducers
benchmark(): Transducer<any, number>
cat<A>(): Transducer<A[], A>
chunkSort<T>(n: number, key?: ((x: T) => any), cmp?: Comparator<any>): Transducer<T, T>
dedupe<T>(equiv?: (a: T, b: T) => boolean): Transducer<T, T>
delayed<T>(t: number): Transducer<T, Promise<T>>
distinct<T>(mapfn?: (x: T) => any): Transducer<T, T>
drop<T>(n: number): Transducer<T, T>
dropNth<T>(n: number): Transducer<T, T>
dropWhile<T>(pred: Predicate<T>): Transducer<T, T>
filter<T>(pred: Predicate<T>): Transducer<T, T>
flatten<T>(): Transducer<T | Iterable<T>, T>
flattenOnly<T>(pred: Predicate<T>): Transducer<T | Iterable<T>, T>
inspect<T>(prefix?: string): Transducer<T, T>
interleave<A, B>(sep: B | (() => B)): Transducer<A, A | B>
interpose<A, B>(sep: B | (() => B)): Transducer<A, A | B>
keep<T>(f?: ((x: T) => any)): Transducer<T, T>
map<A, B>(fn: (x: A) => B): Transducer<A, B>
mapcat<A, B>(fn: (x: A) => Iterable<B>): Transducer<A, B>
mapIndexed<A, B>(fn: (i: number, x: A) => B): Transducer<A, B>
partition<T>(size: number, step?: number, all?: boolean): Transducer<T, T[]>
partitionBy<T>(fn: (x: T) => any): Transducer<T, T[]>
pluck(key: PropertyKey): Transducer<any, any>
repeat<T>(n: number): Transducer<T, T>
sample<T>(prob: number): Transducer<T, T>
scan<A, B>(rfn: Reducer<B, A>, acc?: B): Transducer<A, B>
selectKeys(...keys: PropertyKey[]): Transducer<any, any>
sideEffect<T>(fn: (x: T) => void): Transducer<T, T>
streamShuffle<T>(n: number, maxSwaps?: number): Transducer<T, T>
streamSort<T>(n: number, key?: ((x: T) => any), cmp?: Comparator<any>): Transducer<T, T>
take<T>(n: number): Transducer<T, T>
takeNth<T>(n: number): Transducer<T, T>
takeWhile<T>(pred: Predicate<T>): Transducer<T, T>
throttle<T>(delay: number): Transducer<T, T>
Reducers
add(): Reducer<number, number>
assocMap<A, B>(): Reducer<Map<A, B>, [A, B]>
assocObj<T>(): Reducer<IObjectOf<T>, [PropertyKey, T]>
conj<T>(): Reducer<Set<T>, T>
count(): Reducer<number, number>
frequencies<T>(): Reducer<Map<T, number>, T>
last(): last<T>(): Reducer<T, T>
max(): Reducer<number, number>
mean(): Reducer<number, number>
min(): Reducer<number, number>
mul(): Reducer<number, number>
push<T>(): Reducer<T[], T>
pushCopy<T>(): Reducer<T[], T>
Authors
License
© 2016-2018 Karsten Schmidt // Apache Software License 2.0