Skip to content

Commit

Permalink
refactor(transducers): extract internal helpers, update rfns & xforms
Browse files Browse the repository at this point in the history
Impacted reducers:

- add/mul/sub
- groupByMap/Obj

Xforms:

- movingMedian
- partitionSort / streamSort
- takeLast
  • Loading branch information
postspectacular committed Aug 21, 2019
1 parent 820e7c7 commit 7772f9b
Show file tree
Hide file tree
Showing 15 changed files with 100 additions and 115 deletions.
4 changes: 2 additions & 2 deletions packages/transducers/package.json
Expand Up @@ -20,7 +20,7 @@
"build:test": "rimraf build && tsc -p test/tsconfig.json",
"test": "yarn build:test && mocha build/test/*.js",
"cover": "yarn build:test && nyc mocha build/test/*.js && nyc report --reporter=lcov",
"clean": "rimraf *.js *.d.ts .nyc_output build coverage doc lib func iter rfn xform",
"clean": "rimraf *.js *.d.ts .nyc_output build coverage doc lib func internal iter rfn xform",
"doc": "node_modules/.bin/typedoc --mode modules --out doc --ignoreCompilerErrors src",
"pub": "yarn build:release && yarn publish --access public"
},
Expand Down Expand Up @@ -69,4 +69,4 @@
"access": "public"
},
"sideEffects": false
}
}
22 changes: 22 additions & 0 deletions packages/transducers/src/internal/drain.ts
@@ -0,0 +1,22 @@
import { Fn } from "@thi.ng/api";
import { ReductionFn } from "../api";
import { isReduced } from "../reduced";

/**
* Helper HOF yielding a buffer drain completion function for some
* transducers.
*
* @param buf
* @param complete
* @param reduce
*/
export const __drain = <T>(
buf: T[],
complete: Fn<any, any>,
reduce: ReductionFn<any, T>
) => (acc: T[]) => {
while (buf.length && !isReduced(acc)) {
acc = reduce(acc, buf.shift()!);
}
return complete(acc);
};
17 changes: 17 additions & 0 deletions packages/transducers/src/internal/group-opts.ts
@@ -0,0 +1,17 @@
import { identity } from "@thi.ng/compose";
import { GroupByOpts } from "../api";
import { push } from "../rfn/push";

/**
* Shared helper function for groupBy* reducers
*
* @param opts
*/
export const __groupByOpts = <SRC, KEY, GROUP>(
opts?: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>
) =>
<GroupByOpts<SRC, KEY, GROUP>>{
key: identity,
group: push(),
...opts
};
File renamed without changes.
10 changes: 10 additions & 0 deletions packages/transducers/src/internal/sort-opts.ts
@@ -0,0 +1,10 @@
import { compare } from "@thi.ng/compare";
import { identity } from "@thi.ng/compose";
import { SortOpts } from "../api";

export const __sortOpts = <A, B>(opts?: Partial<SortOpts<A, B>>) =>
<SortOpts<A, B>>{
key: identity,
compare,
...opts
};
2 changes: 1 addition & 1 deletion packages/transducers/src/rfn/add.ts
@@ -1,5 +1,5 @@
import { Reducer } from "../api";
import { __mathop } from "./mathop";
import { __mathop } from "../internal/mathop";

/**
* Reducer to compute sum of values with given `init` value. Default: 0
Expand Down
22 changes: 7 additions & 15 deletions packages/transducers/src/rfn/group-by-map.ts
@@ -1,28 +1,20 @@
import { identity } from "@thi.ng/compose";
import { GroupByOpts, Reducer } from "../api";
import { __groupByOpts } from "../internal/group-opts";
import { $$reduce, reducer } from "../reduce";
import { push } from "./push";

export function groupByMap<SRC, KEY, GROUP>(
opts?: Partial<GroupByOpts<SRC, KEY, GROUP>>
): Reducer<Map<KEY, GROUP>, SRC>;
// prettier-ignore
export function groupByMap<SRC, KEY, GROUP>(opts?: Partial<GroupByOpts<SRC, KEY, GROUP>>): Reducer<Map<KEY, GROUP>, SRC>;
export function groupByMap<SRC, GROUP>(xs: Iterable<SRC>): Map<SRC, GROUP>;
export function groupByMap<SRC, KEY, GROUP>(
opts: Partial<GroupByOpts<SRC, KEY, GROUP>>,
xs: Iterable<SRC>
): Map<KEY, GROUP>;
// prettier-ignore
export function groupByMap<SRC, KEY, GROUP>(opts: Partial<GroupByOpts<SRC, KEY, GROUP>>, xs: Iterable<SRC>): Map<KEY, GROUP>;
export function groupByMap<SRC, KEY, GROUP>(...args: any[]): any {
const res = $$reduce(groupByMap, args);
if (res !== undefined) {
return res;
}
const opts = <GroupByOpts<SRC, KEY, GROUP>>{
key: identity,
group: push(),
...args[0]
};
const opts = __groupByOpts<SRC, KEY, GROUP>(args[0]);
const [init, _, reduce] = opts.group;
_;
_; // ignore
return reducer<Map<KEY, GROUP>, SRC>(
() => new Map(),
(acc, x) => {
Expand Down
26 changes: 9 additions & 17 deletions packages/transducers/src/rfn/group-by-obj.ts
@@ -1,33 +1,25 @@
import { IObjectOf } from "@thi.ng/api";
import { identity } from "@thi.ng/compose";
import { GroupByOpts, Reducer } from "../api";
import { __groupByOpts } from "../internal/group-opts";
import { $$reduce, reducer } from "../reduce";
import { push } from "./push";

export function groupByObj<SRC, GROUP>(
opts?: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>
): Reducer<IObjectOf<GROUP>, SRC>;
// prettier-ignore
export function groupByObj<SRC, GROUP>(opts?: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>): Reducer<IObjectOf<GROUP>, SRC>;
export function groupByObj<SRC>(xs: Iterable<SRC>): IObjectOf<SRC[]>;
export function groupByObj<SRC, GROUP>(
opts: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>,
xs: Iterable<SRC>
): IObjectOf<GROUP>;
// prettier-ignore
export function groupByObj<SRC, GROUP>(opts: Partial<GroupByOpts<SRC, PropertyKey, GROUP>>, xs: Iterable<SRC>): IObjectOf<GROUP>;
export function groupByObj<SRC, GROUP>(...args: any[]): any {
const res = $$reduce(groupByObj, args);
if (res) {
return res;
}
const _opts = <GroupByOpts<SRC, PropertyKey, GROUP>>{
key: identity,
group: push(),
...args[0]
};
const [_init, _, _reduce] = _opts.group;
_;
const opts = __groupByOpts<SRC, PropertyKey, GROUP>(args[0]);
const [_init, _, _reduce] = opts.group;
_; // ignore
return reducer<IObjectOf<GROUP>, SRC>(
() => ({}),
(acc, x: SRC) => {
const k: any = _opts.key(x);
const k: any = opts.key(x);
acc[k] = acc[k]
? <GROUP>_reduce(acc[k], x)
: <GROUP>_reduce(_init(), x);
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/rfn/mul.ts
@@ -1,5 +1,5 @@
import { Reducer } from "../api";
import { __mathop } from "./mathop";
import { __mathop } from "../internal/mathop";

/**
* Reducer to compute product of values with optional `init` value
Expand Down
2 changes: 1 addition & 1 deletion packages/transducers/src/rfn/sub.ts
@@ -1,5 +1,5 @@
import { Reducer } from "../api";
import { __mathop } from "./mathop";
import { __mathop } from "../internal/mathop";

/**
* Reducer to successively subtract values from optional `init` value
Expand Down
6 changes: 2 additions & 4 deletions packages/transducers/src/xform/moving-average.ts
Expand Up @@ -18,10 +18,8 @@ import { iterator1 } from "../iterator";
* @param src
*/
export function movingAverage(period: number): Transducer<number, number>;
export function movingAverage(
period: number,
src: Iterable<number>
): IterableIterator<number>;
// prettier-ignore
export function movingAverage(period: number, src: Iterable<number>): IterableIterator<number>;
export function movingAverage(period: number, src?: Iterable<number>): any {
return src
? iterator1(movingAverage(period), src)
Expand Down
28 changes: 8 additions & 20 deletions packages/transducers/src/xform/moving-median.ts
@@ -1,7 +1,6 @@
import { compare as cmp } from "@thi.ng/compare";
import { identity } from "@thi.ng/compose";
import { SortOpts, Transducer } from "../api";
import { comp } from "../func/comp";
import { __sortOpts } from "../internal/sort-opts";
import { $iter } from "../iterator";
import { map } from "./map";
import { partition } from "./partition";
Expand All @@ -16,29 +15,18 @@ import { partition } from "./partition";
* @param opts
* @param src
*/
export function movingMedian<A, B>(
n: number,
opts?: Partial<SortOpts<A, B>>
): Transducer<A, A>;
export function movingMedian<A, B>(
n: number,
src: Iterable<A>
): IterableIterator<A>;
export function movingMedian<A, B>(
n: number,
opts: Partial<SortOpts<A, B>>,
src: Iterable<A>
): IterableIterator<A>;
// prettier-ignore
export function movingMedian<A, B>(n: number, opts?: Partial<SortOpts<A, B>>): Transducer<A, A>;
// prettier-ignore
export function movingMedian<A, B>(n: number, src: Iterable<A>): IterableIterator<A>;
// prettier-ignore
export function movingMedian<A, B>(n: number, opts: Partial<SortOpts<A, B>>, src: Iterable<A>): IterableIterator<A>;
export function movingMedian<A, B>(...args: any[]): any {
const iter = $iter(movingMedian, args);
if (iter) {
return iter;
}
const { key, compare } = <SortOpts<A, B>>{
key: <any>identity,
compare: cmp,
...args[1]
};
const { key, compare } = __sortOpts<A, B>(args[1]);
const n = args[0];
const m = n >> 1;
return comp<A, A[], A>(
Expand Down
28 changes: 8 additions & 20 deletions packages/transducers/src/xform/partition-sort.ts
@@ -1,7 +1,6 @@
import { compare as cmp } from "@thi.ng/compare";
import { identity } from "@thi.ng/compose";
import { SortOpts, Transducer } from "../api";
import { comp } from "../func/comp";
import { __sortOpts } from "../internal/sort-opts";
import { $iter, iterator } from "../iterator";
import { mapcat } from "./mapcat";
import { partition } from "./partition";
Expand Down Expand Up @@ -32,29 +31,18 @@ import { partition } from "./partition";
* @param key sort key lookup
* @param cmp comparator
*/
export function partitionSort<A, B>(
n: number,
opts?: Partial<SortOpts<A, B>>
): Transducer<A, A>;
export function partitionSort<A, B>(
n: number,
src: Iterable<A>
): IterableIterator<A>;
export function partitionSort<A, B>(
n: number,
opts: Partial<SortOpts<A, B>>,
src: Iterable<A>
): IterableIterator<A>;
// prettier-ignore
export function partitionSort<A, B>(n: number, opts?: Partial<SortOpts<A, B>>): Transducer<A, A>;
// prettier-ignore
export function partitionSort<A, B>(n: number, src: Iterable<A>): IterableIterator<A>;
// prettier-ignore
export function partitionSort<A, B>(n: number, opts: Partial<SortOpts<A, B>>, src: Iterable<A>): IterableIterator<A>;
export function partitionSort<A, B>(...args: any[]): any {
const iter = $iter(partitionSort, args, iterator);
if (iter) {
return iter;
}
const { key, compare } = <SortOpts<A, B>>{
key: <any>identity,
compare: cmp,
...args[1]
};
const { key, compare } = __sortOpts<A, B>(args[1]);
return comp<A, A[], A>(
partition(args[0], true),
mapcat((window: A[]) =>
Expand Down
37 changes: 10 additions & 27 deletions packages/transducers/src/xform/stream-sort.ts
@@ -1,9 +1,8 @@
import { binarySearch } from "@thi.ng/arrays";
import { compare as cmp } from "@thi.ng/compare";
import { identity } from "@thi.ng/compose";
import { Reducer, SortOpts, Transducer } from "../api";
import { __drain } from "../internal/drain";
import { __sortOpts } from "../internal/sort-opts";
import { $iter, iterator } from "../iterator";
import { isReduced } from "../reduced";

/**
* Transducer. Similar to `partitionSort()`, however uses proper sliding
Expand All @@ -19,40 +18,24 @@ import { isReduced } from "../reduced";
* @param key
* @param cmp
*/
export function streamSort<A, B>(
n: number,
opts?: Partial<SortOpts<A, B>>
): Transducer<A, A>;
export function streamSort<A, B>(
n: number,
src: Iterable<A>
): IterableIterator<A>;
export function streamSort<A, B>(
n: number,
opts: Partial<SortOpts<A, B>>,
src: Iterable<A>
): IterableIterator<A>;
// prettier-ignore
export function streamSort<A, B>(n: number, opts?: Partial<SortOpts<A, B>>): Transducer<A, A>;
// prettier-ignore
export function streamSort<A, B>(n: number, src: Iterable<A>): IterableIterator<A>;
// prettier-ignore
export function streamSort<A, B>(n: number, opts: Partial<SortOpts<A, B>>, src: Iterable<A>): IterableIterator<A>;
export function streamSort<A, B>(...args: any[]): any {
const iter = $iter(streamSort, args, iterator);
if (iter) {
return iter;
}
const { key, compare } = <SortOpts<A, B>>{
key: <any>identity,
compare: cmp,
...args[1]
};
const { key, compare } = __sortOpts<A, B>(args[1]);
const n = args[0];
return ([init, complete, reduce]: Reducer<any, A>) => {
const buf: A[] = [];
return <Reducer<any, A>>[
init,
(acc) => {
while (buf.length && !isReduced(acc)) {
acc = reduce(acc, buf.shift()!);
}
return complete(acc);
},
__drain(buf, complete, reduce),
(acc, x) => {
const idx = binarySearch(buf, x, key, compare);
buf.splice(idx < 0 ? -(idx + 1) : idx, 0, x);
Expand Down
9 changes: 2 additions & 7 deletions packages/transducers/src/xform/take-last.ts
@@ -1,6 +1,6 @@
import { Reducer, Transducer } from "../api";
import { __drain } from "../internal/drain";
import { iterator } from "../iterator";
import { isReduced } from "../reduced";

/**
* Transducer which only yields the last `n` values. Assumes
Expand All @@ -23,12 +23,7 @@ export function takeLast<T>(n: number, src?: Iterable<T>): any {
const buf: T[] = [];
return <Reducer<any, T>>[
init,
(acc) => {
while (buf.length && !isReduced(acc)) {
acc = reduce(acc, buf.shift()!);
}
return complete(acc);
},
__drain(buf, complete, reduce),
(acc, x) => {
if (buf.length === n) {
buf.shift();
Expand Down

0 comments on commit 7772f9b

Please sign in to comment.