@thi.ng/transducers
Lightweight transducer implementations for ES6 / TypeScript (8KB minified, full lib).
The library provides 33 transducers and 14 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
import * as tx from "@thi.ng/transducers";
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.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, "hello world")
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.
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 only partition
, partitionBy
, streamSort
, streamShuffle
make
use of the 1-arity completing function.
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
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
iterator<A, B>(tx: Transducer<A, B>, xs: Iterable<A>): IterableIterator<B>
comp(f1, f2, ...)
compR(rfn: Reducer<any, any>, fn: (acc, x) => any): Reducer<any, any>
Transducers
map<A, B>(fn: (x: A) => B): Transducer<A, B>
mapIndexed<A, B>(fn: (i: number, x: A) => B): Transducer<A, B>
mapcat<A, B>(fn: (x: A) => Iterable<B>): Transducer<A, B>
cat<A>(): Transducer<A[], A>
flatten<T>(): Transducer<T | Iterable<T>, T>
flattenOnly<T>(pred: Predicate<T>): Transducer<T | Iterable<T>, T>
selectKeys(...keys: PropertyKey[]): Transducer<any, any>
pluck(key: PropertyKey): Transducer<any, any>
scan<A, B>(rfn: Reducer<B, A>, acc?: B): Transducer<A, B>
filter<T>(pred: Predicate<T>): Transducer<T, T>
keep<T>(f?: ((x: T) => any)): Transducer<T, T>
throttle<T>(delay: number): Transducer<T, T>
delayed<T>(t: number): Transducer<T, Promise<T>>
bench(): Transducer<any, number>
sideEffect<T>(fn: (x: T) => void): Transducer<T, T>
inspect<T>(prefix?: string): Transducer<T, T>
distinct<T>(mapfn?: (x: T) => any): Transducer<T, T>
dedupe<T>(equiv?: (a: T, b: T) => boolean): Transducer<T, T>
interpose<A, B>(sep: B | (() => B)): Transducer<A, A | B>
interleave<A, B>(sep: B | (() => B)): Transducer<A, A | B>
take<T>(n: number): Transducer<T, T>
takeWhile<T>(pred: Predicate<T>): Transducer<T, T>
takeNth<T>(n: number): Transducer<T, T>
drop<T>(n: number): Transducer<T, T>
dropWhile<T>(pred: Predicate<T>): Transducer<T, T>
dropNth<T>(n: number): Transducer<T, T>
repeat<T>(n: number): Transducer<T, T>
sample<T>(prob: number): Transducer<T, T>
partition<T>(size: number, step?: number, all?: boolean): Transducer<T, T[]>
partitionBy<T>(fn: (x: T) => any): Transducer<T, T[]>
chunkSort<T>(n: number, key?: ((x: T) => any), cmp?: Comparator<any>): Transducer<T, T>
streamSort<T>(n: number, key?: ((x: T) => any), cmp?: Comparator<any>): Transducer<T, T>
streamShuffle<T>(n: number, maxSwaps?: number): Transducer<T, T>
Reducers
push: Reducer<any[], any>
pushCopy: Reducer<any[], any>
conj: Reducer<Set<any>, any>
assocObj: Reducer<any, [PropertyKey, any]>
assocMap: Reducer<Map<any, any>, [any, any]>
add: Reducer<number, number>
count: Reducer<number, number>
mul: Reducer<number, number>
min: Reducer<number, number>
max: Reducer<number, number>
frequencies: Reducer<Map<any, number>, any
Authors
License
© 2016-2018 Karsten Schmidt // Apache Software License 2.0