From ef7a798d35c172d50b2f4453f4522260d0fe81e4 Mon Sep 17 00:00:00 2001 From: Gavin Cannizzaro Date: Mon, 1 Jun 2020 03:48:20 -0400 Subject: [PATCH] fix(transducers): #186, Fix crash when using empty string as source for several transducers. --- packages/transducers/src/xform/benchmark.ts | 3 +- packages/transducers/src/xform/drop-nth.ts | 3 +- packages/transducers/src/xform/drop.ts | 3 +- packages/transducers/src/xform/duplicate.ts | 3 +- packages/transducers/src/xform/filter.ts | 3 +- .../transducers/src/xform/flatten-with.ts | 5 +-- packages/transducers/src/xform/interleave.ts | 3 +- packages/transducers/src/xform/interpolate.ts | 3 +- packages/transducers/src/xform/interpose.ts | 3 +- packages/transducers/src/xform/labeled.ts | 4 +-- packages/transducers/src/xform/map-deep.ts | 5 ++- packages/transducers/src/xform/map.ts | 3 +- packages/transducers/src/xform/mapcat.ts | 3 +- packages/transducers/src/xform/match-first.ts | 3 +- packages/transducers/src/xform/match-last.ts | 3 +- .../transducers/src/xform/moving-average.ts | 3 +- packages/transducers/src/xform/pad-last.ts | 3 +- .../transducers/src/xform/partition-of.ts | 3 +- .../transducers/src/xform/partition-time.ts | 3 +- packages/transducers/src/xform/pluck.ts | 5 ++- packages/transducers/src/xform/select-keys.ts | 5 ++- packages/transducers/src/xform/struct.ts | 3 +- packages/transducers/src/xform/swizzle.ts | 5 ++- packages/transducers/src/xform/take-last.ts | 3 +- packages/transducers/src/xform/take-nth.ts | 3 +- packages/transducers/src/xform/take.ts | 3 +- .../transducers/src/xform/throttle-time.ts | 3 +- packages/transducers/src/xform/throttle.ts | 3 +- packages/transducers/src/xform/toggle.ts | 3 +- packages/transducers/test/drop.ts | 21 +++++++++++++ packages/transducers/test/filter.ts | 31 +++++++++++++++++++ packages/transducers/test/flatten.ts | 5 +++ packages/transducers/test/map.ts | 18 +++++++++++ packages/transducers/test/take.ts | 21 +++++++++++++ 34 files changed, 163 insertions(+), 31 deletions(-) create mode 100644 packages/transducers/test/drop.ts create mode 100644 packages/transducers/test/filter.ts create mode 100644 packages/transducers/test/map.ts create mode 100644 packages/transducers/test/take.ts diff --git a/packages/transducers/src/xform/benchmark.ts b/packages/transducers/src/xform/benchmark.ts index 11a9f78020..6386d1d8bd 100644 --- a/packages/transducers/src/xform/benchmark.ts +++ b/packages/transducers/src/xform/benchmark.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; import type { Reducer, Transducer } from "../api"; @@ -21,7 +22,7 @@ import type { Reducer, Transducer } from "../api"; export function benchmark(): Transducer; export function benchmark(src: Iterable): IterableIterator; export function benchmark(src?: Iterable): any { - return src + return isIterable(src) ? iterator1(benchmark(), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/drop-nth.ts b/packages/transducers/src/xform/drop-nth.ts index 3d7fdc22ad..7b53fa8e9d 100644 --- a/packages/transducers/src/xform/drop-nth.ts +++ b/packages/transducers/src/xform/drop-nth.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { throttle } from "./throttle"; import type { Transducer } from "../api"; @@ -5,7 +6,7 @@ import type { Transducer } from "../api"; export function dropNth(n: number): Transducer; export function dropNth(n: number, src: Iterable): IterableIterator; export function dropNth(n: number, src?: Iterable): any { - if (src) { + if (isIterable(src)) { return iterator1(dropNth(n), src); } n = Math.max(0, n - 1); diff --git a/packages/transducers/src/xform/drop.ts b/packages/transducers/src/xform/drop.ts index 33d41c151c..c8beb49de3 100644 --- a/packages/transducers/src/xform/drop.ts +++ b/packages/transducers/src/xform/drop.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; import type { Reducer, Transducer } from "../api"; @@ -5,7 +6,7 @@ import type { Reducer, Transducer } from "../api"; export function drop(n: number): Transducer; export function drop(n: number, src: Iterable): IterableIterator; export function drop(n: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(drop(n), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/duplicate.ts b/packages/transducers/src/xform/duplicate.ts index ad812ac590..192ef79712 100644 --- a/packages/transducers/src/xform/duplicate.ts +++ b/packages/transducers/src/xform/duplicate.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator } from "../iterator"; import { isReduced } from "../reduced"; @@ -6,7 +7,7 @@ import type { Reducer, Transducer } from "../api"; export function duplicate(n?: number): Transducer; export function duplicate(n: number, src: Iterable): IterableIterator; export function duplicate(n = 1, src?: Iterable): any { - return src + return isIterable(src) ? iterator(duplicate(n), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/filter.ts b/packages/transducers/src/xform/filter.ts index da8ddfb9c8..985c788b23 100644 --- a/packages/transducers/src/xform/filter.ts +++ b/packages/transducers/src/xform/filter.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; import type { Predicate } from "@thi.ng/api"; @@ -9,7 +10,7 @@ export function filter( src: Iterable ): IterableIterator; export function filter(pred: Predicate, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(filter(pred), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/flatten-with.ts b/packages/transducers/src/xform/flatten-with.ts index 990831c6a9..3a2d2db90d 100644 --- a/packages/transducers/src/xform/flatten-with.ts +++ b/packages/transducers/src/xform/flatten-with.ts @@ -1,3 +1,4 @@ +import { isIterable, isString } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator } from "../iterator"; import { isReduced } from "../reduced"; @@ -15,8 +16,8 @@ export function flattenWith( fn: Fn>, src?: Iterable> ): any { - return src - ? iterator(flattenWith(fn), src) + return isIterable(src) + ? iterator(flattenWith(fn), isString(src) ? [src] : src) : (rfn: Reducer) => { const reduce = rfn[2]; const flatten = (acc: any, x: any) => { diff --git a/packages/transducers/src/xform/interleave.ts b/packages/transducers/src/xform/interleave.ts index 33259e5d14..6e8deb2002 100644 --- a/packages/transducers/src/xform/interleave.ts +++ b/packages/transducers/src/xform/interleave.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator } from "../iterator"; import { isReduced } from "../reduced"; @@ -10,7 +11,7 @@ export function interleave( src: Iterable ): IterableIterator; export function interleave(sep: any, src?: Iterable): any { - return src + return isIterable(src) ? iterator(interleave(sep), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/interpolate.ts b/packages/transducers/src/xform/interpolate.ts index 8c65299c5d..17cdad71cb 100644 --- a/packages/transducers/src/xform/interpolate.ts +++ b/packages/transducers/src/xform/interpolate.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { comp } from "../func/comp"; import { normRange } from "../iter/norm-range"; import { iterator } from "../iterator"; @@ -51,7 +52,7 @@ export function interpolate(fn: Fn2, window: number, n: numbe export function interpolate(fn: Fn2, window: number, n: number, src: Iterable): IterableIterator; // prettier-ignore export function interpolate(fn: Fn2, window: number, n: number, src?: Iterable) { - return src + return isIterable(src) ? iterator(interpolate(fn, window, n), src) : comp( partition(window, 1), diff --git a/packages/transducers/src/xform/interpose.ts b/packages/transducers/src/xform/interpose.ts index 9a6421c3b4..5f1855e642 100644 --- a/packages/transducers/src/xform/interpose.ts +++ b/packages/transducers/src/xform/interpose.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator } from "../iterator"; import { isReduced } from "../reduced"; @@ -10,7 +11,7 @@ export function interpose( src: Iterable ): IterableIterator; export function interpose(sep: any, src?: Iterable): any { - return src + return isIterable(src) ? iterator(interpose(sep), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/labeled.ts b/packages/transducers/src/xform/labeled.ts index 5d158ff830..4222d291de 100644 --- a/packages/transducers/src/xform/labeled.ts +++ b/packages/transducers/src/xform/labeled.ts @@ -1,4 +1,4 @@ -import { isFunction } from "@thi.ng/checks"; +import { isFunction, isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { map } from "./map"; import type { Transducer } from "../api"; @@ -11,7 +11,7 @@ export function labeled( src: Iterable ): IterableIterator<[L, T]>; export function labeled(id: LabelFn, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(labeled(id), src) : map(isFunction(id) ? (x: T) => [id(x), x] : (x: T) => [id, x]); } diff --git a/packages/transducers/src/xform/map-deep.ts b/packages/transducers/src/xform/map-deep.ts index a682262b00..f0ecdc53fc 100644 --- a/packages/transducers/src/xform/map-deep.ts +++ b/packages/transducers/src/xform/map-deep.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { deepTransform } from "../func/deep-transform"; import { iterator1 } from "../iterator"; import { map } from "./map"; @@ -17,5 +18,7 @@ export function mapDeep( src: Iterable ): IterableIterator; export function mapDeep(spec: TransformSpec, src?: Iterable): any { - return src ? iterator1(mapDeep(spec), src) : map(deepTransform(spec)); + return isIterable(src) + ? iterator1(mapDeep(spec), src) + : map(deepTransform(spec)); } diff --git a/packages/transducers/src/xform/map.ts b/packages/transducers/src/xform/map.ts index a3896d7dd7..82b4279bf9 100644 --- a/packages/transducers/src/xform/map.ts +++ b/packages/transducers/src/xform/map.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; import type { Fn } from "@thi.ng/api"; @@ -18,7 +19,7 @@ import type { Reducer, Transducer } from "../api"; export function map(fn: Fn): Transducer; export function map(fn: Fn, src: Iterable): IterableIterator; export function map(fn: Fn, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(map(fn), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/mapcat.ts b/packages/transducers/src/xform/mapcat.ts index 3295191acd..ae08e9c056 100644 --- a/packages/transducers/src/xform/mapcat.ts +++ b/packages/transducers/src/xform/mapcat.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { comp } from "../func/comp"; import { iterator } from "../iterator"; import { cat } from "./cat"; @@ -33,5 +34,5 @@ export function mapcat( fn: Fn | null | undefined>, src?: Iterable ): any { - return src ? iterator(mapcat(fn), src) : comp(map(fn), cat()); + return isIterable(src) ? iterator(mapcat(fn), src) : comp(map(fn), cat()); } diff --git a/packages/transducers/src/xform/match-first.ts b/packages/transducers/src/xform/match-first.ts index 3bfdd91433..23c2778cfc 100644 --- a/packages/transducers/src/xform/match-first.ts +++ b/packages/transducers/src/xform/match-first.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { comp } from "../func/comp"; import { iterator1 } from "../iterator"; import { filter } from "./filter"; @@ -41,7 +42,7 @@ export function matchFirst( src: Iterable ): T | undefined; export function matchFirst(pred: Predicate, src?: Iterable): any { - return src + return isIterable(src) ? [...iterator1(matchFirst(pred), src)][0] : comp(filter(pred), take(1)); } diff --git a/packages/transducers/src/xform/match-last.ts b/packages/transducers/src/xform/match-last.ts index 8d25dc0fa4..1b6bdaa6cf 100644 --- a/packages/transducers/src/xform/match-last.ts +++ b/packages/transducers/src/xform/match-last.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { comp } from "../func/comp"; import { iterator } from "../iterator"; import { filter } from "./filter"; @@ -41,7 +42,7 @@ export function matchLast( src: Iterable ): T | undefined; export function matchLast(pred: Predicate, src?: Iterable): any { - return src + return isIterable(src) ? [...iterator(matchLast(pred), src)][0] : comp(filter(pred), takeLast(1)); } diff --git a/packages/transducers/src/xform/moving-average.ts b/packages/transducers/src/xform/moving-average.ts index 9ce949f470..7734c9044f 100644 --- a/packages/transducers/src/xform/moving-average.ts +++ b/packages/transducers/src/xform/moving-average.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { illegalArgs } from "@thi.ng/errors"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; @@ -24,7 +25,7 @@ export function movingAverage(period: number): Transducer; // prettier-ignore export function movingAverage(period: number, src: Iterable): IterableIterator; export function movingAverage(period: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(movingAverage(period), src) : (rfn: Reducer) => { period |= 0; diff --git a/packages/transducers/src/xform/pad-last.ts b/packages/transducers/src/xform/pad-last.ts index 28768f4983..a19a5c1955 100644 --- a/packages/transducers/src/xform/pad-last.ts +++ b/packages/transducers/src/xform/pad-last.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator } from "../iterator"; import { isReduced } from "../reduced"; import type { Reducer, Transducer } from "../api"; @@ -41,7 +42,7 @@ export function padLast( src: Iterable ): IterableIterator; export function padLast(n: number, fill: T, src?: Iterable): any { - return src + return isIterable(src) ? iterator(padLast(n, fill), src) : ([init, complete, reduce]: Reducer) => { let m = 0; diff --git a/packages/transducers/src/xform/partition-of.ts b/packages/transducers/src/xform/partition-of.ts index e35f2ad6ee..bc8cddbdbe 100644 --- a/packages/transducers/src/xform/partition-of.ts +++ b/packages/transducers/src/xform/partition-of.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator } from "../iterator"; import { partitionBy } from "./partition-by"; import type { Transducer } from "../api"; @@ -26,7 +27,7 @@ export function partitionOf( src: Iterable ): IterableIterator; export function partitionOf(sizes: number[], src?: Iterable): any { - return src + return isIterable(src) ? iterator(partitionOf(sizes), src) : partitionBy(() => { let i = 0, diff --git a/packages/transducers/src/xform/partition-time.ts b/packages/transducers/src/xform/partition-time.ts index b4e33f814a..b54c291b79 100644 --- a/packages/transducers/src/xform/partition-time.ts +++ b/packages/transducers/src/xform/partition-time.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator } from "../iterator"; import { partitionBy } from "./partition-by"; import type { Transducer } from "../api"; @@ -38,7 +39,7 @@ export function partitionTime( src: Iterable ): IterableIterator; export function partitionTime(period: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator(partitionTime(period), src) : partitionBy(() => { let last = 0; diff --git a/packages/transducers/src/xform/pluck.ts b/packages/transducers/src/xform/pluck.ts index 0d9804d21e..dbfe37dfdc 100644 --- a/packages/transducers/src/xform/pluck.ts +++ b/packages/transducers/src/xform/pluck.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { map } from "./map"; import type { Transducer } from "../api"; @@ -20,5 +21,7 @@ export function pluck( src: Iterable ): IterableIterator; export function pluck(key: PropertyKey, src?: Iterable): any { - return src ? iterator1(pluck(key), src) : map((x: any) => x[key]); + return isIterable(src) + ? iterator1(pluck(key), src) + : map((x: any) => x[key]); } diff --git a/packages/transducers/src/xform/select-keys.ts b/packages/transducers/src/xform/select-keys.ts index 2e5f03e995..975df3b1e3 100644 --- a/packages/transducers/src/xform/select-keys.ts +++ b/packages/transducers/src/xform/select-keys.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { keySelector } from "../func/key-selector"; import { iterator1 } from "../iterator"; import { map } from "./map"; @@ -33,5 +34,7 @@ export function selectKeys( src: Iterable ): IterableIterator; export function selectKeys(keys: PropertyKey[], src?: Iterable): any { - return src ? iterator1(selectKeys(keys), src) : map(keySelector(keys)); + return isIterable(src) + ? iterator1(selectKeys(keys), src) + : map(keySelector(keys)); } diff --git a/packages/transducers/src/xform/struct.ts b/packages/transducers/src/xform/struct.ts index c981fb8123..5326fb7134 100644 --- a/packages/transducers/src/xform/struct.ts +++ b/packages/transducers/src/xform/struct.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { comp } from "../func/comp"; import { iterator } from "../iterator"; import { mapKeys } from "./map-keys"; @@ -52,7 +53,7 @@ export function struct( src: Iterable ): IterableIterator; export function struct(fields: StructField[], src?: Iterable): any { - return src + return isIterable(src) ? iterator(struct(fields), src) : comp( partitionOf(fields.map((f) => f[1])), diff --git a/packages/transducers/src/xform/swizzle.ts b/packages/transducers/src/xform/swizzle.ts index 32a042ec1c..e216dd7e76 100644 --- a/packages/transducers/src/xform/swizzle.ts +++ b/packages/transducers/src/xform/swizzle.ts @@ -1,4 +1,5 @@ import { swizzle as _swizzle } from "@thi.ng/arrays"; +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { map } from "./map"; import type { Transducer } from "../api"; @@ -30,5 +31,7 @@ export function swizzle( src: Iterable ): IterableIterator; export function swizzle(order: PropertyKey[], src?: Iterable): any { - return src ? iterator1(swizzle(order), src) : map(_swizzle(order)); + return isIterable(src) + ? iterator1(swizzle(order), src) + : map(_swizzle(order)); } diff --git a/packages/transducers/src/xform/take-last.ts b/packages/transducers/src/xform/take-last.ts index 84237b6ba4..b82312c7c8 100644 --- a/packages/transducers/src/xform/take-last.ts +++ b/packages/transducers/src/xform/take-last.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { __drain } from "../internal/drain"; import { iterator } from "../iterator"; import type { Reducer, Transducer } from "../api"; @@ -18,7 +19,7 @@ import type { Reducer, Transducer } from "../api"; export function takeLast(n: number): Transducer; export function takeLast(n: number, src: Iterable): IterableIterator; export function takeLast(n: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator(takeLast(n), src) : ([init, complete, reduce]: Reducer) => { const buf: T[] = []; diff --git a/packages/transducers/src/xform/take-nth.ts b/packages/transducers/src/xform/take-nth.ts index ff048203b0..6b5ab5da0b 100644 --- a/packages/transducers/src/xform/take-nth.ts +++ b/packages/transducers/src/xform/take-nth.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { throttle } from "./throttle"; import type { Transducer } from "../api"; @@ -17,7 +18,7 @@ import type { Transducer } from "../api"; export function takeNth(n: number): Transducer; export function takeNth(n: number, src: Iterable): IterableIterator; export function takeNth(n: number, src?: Iterable): any { - if (src) { + if (isIterable(src)) { return iterator1(takeNth(n), src); } n = Math.max(0, n - 1); diff --git a/packages/transducers/src/xform/take.ts b/packages/transducers/src/xform/take.ts index 2ab7d646b5..b706b388f0 100644 --- a/packages/transducers/src/xform/take.ts +++ b/packages/transducers/src/xform/take.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator } from "../iterator"; import { ensureReduced, reduced } from "../reduced"; @@ -18,7 +19,7 @@ import type { Reducer, Transducer } from "../api"; export function take(n: number): Transducer; export function take(n: number, src: Iterable): IterableIterator; export function take(n: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator(take(n), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/throttle-time.ts b/packages/transducers/src/xform/throttle-time.ts index d2efa2ca5c..712ebf76b1 100644 --- a/packages/transducers/src/xform/throttle-time.ts +++ b/packages/transducers/src/xform/throttle-time.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import { throttle } from "./throttle"; import type { Transducer } from "../api"; @@ -22,7 +23,7 @@ export function throttleTime( src: Iterable ): IterableIterator; export function throttleTime(delay: number, src?: Iterable): any { - return src + return isIterable(src) ? iterator1(throttleTime(delay), src) : throttle(() => { let last = 0; diff --git a/packages/transducers/src/xform/throttle.ts b/packages/transducers/src/xform/throttle.ts index b52d29e07d..e981b1b696 100644 --- a/packages/transducers/src/xform/throttle.ts +++ b/packages/transducers/src/xform/throttle.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { compR } from "../func/compr"; import { iterator1 } from "../iterator"; import type { StatefulPredicate } from "@thi.ng/api"; @@ -29,7 +30,7 @@ export function throttle( pred: StatefulPredicate, src?: Iterable ): any { - return src + return isIterable(src) ? iterator1(throttle(pred), src) : (rfn: Reducer) => { const r = rfn[2]; diff --git a/packages/transducers/src/xform/toggle.ts b/packages/transducers/src/xform/toggle.ts index 8068471946..dabc5c4625 100644 --- a/packages/transducers/src/xform/toggle.ts +++ b/packages/transducers/src/xform/toggle.ts @@ -1,3 +1,4 @@ +import { isIterable } from "@thi.ng/checks"; import { iterator1 } from "../iterator"; import type { Reducer, Transducer } from "../api"; @@ -33,7 +34,7 @@ export function toggle( initial = false, src?: Iterable ): any { - return src + return isIterable(src) ? iterator1(toggle(on, off, initial), src) : ([init, complete, reduce]: Reducer) => { let state = initial; diff --git a/packages/transducers/test/drop.ts b/packages/transducers/test/drop.ts new file mode 100644 index 0000000000..f40d680a7d --- /dev/null +++ b/packages/transducers/test/drop.ts @@ -0,0 +1,21 @@ +import { drop, range } from "../src"; + +import * as assert from "assert"; + +describe("drop", () => { + it("starts iterating after N items", () => { + assert.deepEqual([...drop(0, [true, false])], [true, false]); + assert.deepEqual([...drop(1, [true, false])], [false]); + assert.deepEqual([...drop(2, [true, false])], []); + assert.deepEqual([...drop(3, [true, false])], []); + assert.deepEqual([...drop(2, range(0, 4))], [2, 3]); + assert.deepEqual([...drop(0, ["", "ab", "c"])], ["", "ab", "c"]); + assert.deepEqual([...drop(1, ["", "ab", "c"])], ["ab", "c"]); + assert.deepEqual([...drop(2, ["", "ab", "c"])], ["c"]); + assert.deepEqual([...drop(0, "")], []); + assert.deepEqual([...drop(1, "")], []); + assert.deepEqual([...drop(0, "abc")], ["a", "b", "c"]); + assert.deepEqual([...drop(1, "abc")], ["b", "c"]); + assert.deepEqual([...drop(2, "abc")], ["c"]); + }); +}); diff --git a/packages/transducers/test/filter.ts b/packages/transducers/test/filter.ts new file mode 100644 index 0000000000..0d5dad12e4 --- /dev/null +++ b/packages/transducers/test/filter.ts @@ -0,0 +1,31 @@ +import { filter, range } from "../src"; + +import * as assert from "assert"; + +describe("filter", () => { + const identity = (x: any) => x; + const always = () => true; + const never = () => false; + const vowel = (s: string) => /[aeiou]/.test(s); + const even = (n: number) => n % 2 === 0; + + it("applies predicate over iterable and forwards values testing truthy", () => { + assert.deepEqual( + [...filter(identity, [true, false, "a", "", 0, 1, []])], + [true, "a", 1, []] + ); + assert.deepEqual( + [...filter(always, [true, false, "a", "", 0, 1, []])], + [true, false, "a", "", 0, 1, []] + ); + assert.deepEqual( + [...filter(never, [true, false, "a", "", 0, 1, []])], + [] + ); + assert.deepEqual([...filter(vowel, ["", "a", "bc"])], ["a"]); + assert.deepEqual([...filter(even, range(1, 5))], [2, 4]); + assert.deepEqual([...filter(always, "")], []); + assert.deepEqual([...filter(always, "abc")], ["a", "b", "c"]); + assert.deepEqual([...filter(vowel, "abc")], ["a"]); + }); +}); diff --git a/packages/transducers/test/flatten.ts b/packages/transducers/test/flatten.ts index 2ae2f686f7..f2f2c1661a 100644 --- a/packages/transducers/test/flatten.ts +++ b/packages/transducers/test/flatten.ts @@ -14,6 +14,11 @@ describe("flatten", () => { it("strings", () => { assert.deepEqual([...flatten(["", "a"])], ["", "a"]); assert.deepEqual([...flatten([[], ["a"], ""])], ["a", ""]); + assert.deepEqual([...flatten([["abc"]])], ["abc"]); + assert.deepEqual([...flatten(["abc"])], ["abc"]); + assert.deepEqual([...flatten("abc")], ["abc"]); + assert.deepEqual([...flatten([""])], [""]); + assert.deepEqual([...flatten("")], [""]); }); it("iterators", () => { assert.deepEqual([...flatten(range(0))], []); diff --git a/packages/transducers/test/map.ts b/packages/transducers/test/map.ts new file mode 100644 index 0000000000..56f0fdc262 --- /dev/null +++ b/packages/transducers/test/map.ts @@ -0,0 +1,18 @@ +import { map, range } from "../src"; + +import * as assert from "assert"; + +describe("map", () => { + const identity = (t: T): T => t; + const upper = (s: string) => s.toUpperCase(); + const square = (n: number) => n * n; + + it("applies function over iterable", () => { + assert.deepEqual([...map(identity, [])], []); + assert.deepEqual([...map(identity, ["", "ab", "c"])], ["", "ab", "c"]); + assert.deepEqual([...map(upper, ["", "ab", "c"])], ["", "AB", "C"]); + assert.deepEqual([...map(square, range(1, 4))], [1, 4, 9]); + assert.deepEqual([...map(upper, "")], []); + assert.deepEqual([...map(upper, "abc")], ["A", "B", "C"]); + }); +}); diff --git a/packages/transducers/test/take.ts b/packages/transducers/test/take.ts new file mode 100644 index 0000000000..ac5bfbd53b --- /dev/null +++ b/packages/transducers/test/take.ts @@ -0,0 +1,21 @@ +import { take, range } from "../src"; + +import * as assert from "assert"; + +describe("take", () => { + it("iterates up to N items", () => { + assert.deepEqual([...take(0, [true, false])], []); + assert.deepEqual([...take(1, [true, false])], [true]); + assert.deepEqual([...take(2, [true, false])], [true, false]); + assert.deepEqual([...take(3, [true, false])], [true, false]); + assert.deepEqual([...take(2, range(0, 4))], [0, 1]); + assert.deepEqual([...take(0, ["", "ab", "c"])], []); + assert.deepEqual([...take(1, ["", "ab", "c"])], [""]); + assert.deepEqual([...take(2, ["", "ab", "c"])], ["", "ab"]); + assert.deepEqual([...take(0, "")], []); + assert.deepEqual([...take(1, "")], []); + assert.deepEqual([...take(0, "abc")], []); + assert.deepEqual([...take(1, "abc")], ["a"]); + assert.deepEqual([...take(2, "abc")], ["a", "b"]); + }); +});