diff --git a/README.md b/README.md index 9f96fdc..a753855 100644 --- a/README.md +++ b/README.md @@ -10,9 +10,66 @@ as possible. ## Usage -Modules exported from the root of the package are for synchronous iterables -(`Iterable`). Modules exported from the `async` submodule of the package are for -both synchronous and asynchronous iterables (`Iterable` and `AsyncIterable`). +To apply single operation to an iterable, use modules under the root. + +```ts +import { map } from "@core/iterutil/map"; + +const iter = map([1, 2, 3], (v) => v * 2); +console.log(Array.from(iter)); // [2, 4, 6] +``` + +To apply single asynchronous operation to an (async) iterable, use modules under +the `async` submodule. + +```ts +import { map } from "@core/iterutil/async/map"; + +const iter = map([1, 2, 3], (v) => Promise.resolve(v * 2)); +console.log(await Array.fromAsync(iter)); // [2, 4, 6] +``` + +To apply multiple operations to an iterable, use the modules under the `pipe` +submodule with the [@core/pipe] package. + +```ts +import { pipe } from "@core/pipe"; +import { cycle } from "@core/iterutil/pipe/cycle"; +import { filter } from "@core/iterutil/pipe/filter"; +import { map } from "@core/iterutil/pipe/map"; +import { take } from "@core/iterutil/pipe/take"; + +const iter = pipe( + [1, 2, 3], + map((v) => v * 2), + cycle, + take(10), + filter((v) => v % 2 === 0), +); +console.log(Array.from(iter)); // [2, 4, 6, 2, 4, 6, 2, 4, 6, 2] +``` + +To apply multiple asynchronous operations to an (async) iterable, use the +modules under the `pipe/async` submodule with the [@core/pipe] package. + +```ts +import { pipe } from "@core/pipe"; +import { cycle } from "@core/iterutil/pipe/async/cycle"; +import { filter } from "@core/iterutil/pipe/async/filter"; +import { map } from "@core/iterutil/pipe/async/map"; +import { take } from "@core/iterutil/pipe/async/take"; + +const iter = pipe( + [1, 2, 3], + map((v) => Promise.resolve(v * 2)), + cycle, + take(10), + filter((v) => Promise.resolve(v % 2 === 0)), +); +console.log(await Array.fromAsync(iter)); // [2, 4, 6, 2, 4, 6, 2, 4, 6, 2] +``` + +[@core/pipe]: https://jsr.io/@core/pipe ### chain @@ -32,6 +89,30 @@ const iter = chain([1, 2], ["a", "b"], [true, false]); console.log(await Array.fromAsync(iter)); // [1, 2, "a", "b", true, false] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { chain } from "@core/iterutil/pipe/chain"; + +const iter = pipe( + [1, 2], + chain(["a", "b"], [true, false]), +); +console.log(Array.from(iter)); // [1, 2, "a", "b", true, false] +``` + +```ts +import { pipe } from "@core/pipe"; +import { chain } from "@core/iterutil/pipe/async/chain"; + +const iter = pipe( + [1, 2], + chain(["a", "b"], [true, false]), +); +console.log(await Array.fromAsync(iter)); // [1, 2, "a", "b", true, false] +``` + ### chunked Chunks an iterable into arrays of a given size. @@ -50,6 +131,30 @@ const iter = chunked([1, 2, 3, 4, 5], 2); console.log(await Array.fromAsync(iter)); // [[1, 2], [3, 4], [5]] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { chunked } from "@core/iterutil/pipe/chunked"; + +const iter = pipe( + [1, 2, 3, 4, 5], + chunked(2), +); +console.log(Array.from(iter)); // [[1, 2], [3, 4], [5]] +``` + +```ts +import { pipe } from "@core/pipe"; +import { chunked } from "@core/iterutil/pipe/async/chunked"; + +const iter = pipe( + [1, 2, 3, 4, 5], + chunked(2), +); +console.log(await Array.fromAsync(iter)); // [[1, 2], [3, 4], [5]] +``` + ### compact Removes all nullish (`null` or `undefined`) values from an iterable. @@ -68,6 +173,30 @@ const iter = compact([1, undefined, 2, null, 3]); console.log(await Array.fromAsync(iter)); // [1, 2, 3] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { compact } from "@core/iterutil/pipe/compact"; + +const iter = pipe( + [1, undefined, 2, null, 3], + compact, +); +console.log(Array.from(iter)); // [1, 2, 3] +``` + +```ts +import { pipe } from "@core/pipe"; +import { compact } from "@core/iterutil/pipe/async/compact"; + +const iter = pipe( + [1, undefined, 2, null, 3], + compact, +); +console.log(await Array.fromAsync(iter)); // [1, 2, 3] +``` + ### compress Compresses an iterable by selecting elements using a selector iterable. @@ -92,6 +221,30 @@ const iter = compress( console.log(await Array.fromAsync(iter)); // [1, 3, 5] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { compress } from "@core/iterutil/pipe/compress"; + +const iter = pipe( + [1, 2, 3, 4, 5], + compress([true, false, true, false, true]), +); +console.log(Array.from(iter)); // [1, 3, 5] +``` + +```ts +import { pipe } from "@core/pipe"; +import { compress } from "@core/iterutil/pipe/async/compress"; + +const iter = pipe( + [1, 2, 3, 4, 5], + compress([true, false, true, false, true]), +); +console.log(await Array.fromAsync(iter)); // [1, 3, 5] +``` + ### count Generates an infinite sequence of numbers starting from `start` with a step of @@ -125,6 +278,34 @@ const iter = cycle([1, 2, 3]); console.log(await Array.fromAsync(take(iter, 5))); // [1, 2, 3, 1, 2] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { cycle } from "@core/iterutil/pipe/cycle"; +import { take } from "@core/iterutil/pipe/take"; + +const iter = pipe( + [1, 2, 3], + cycle, + take(5), +); +console.log(Array.from(iter)); // [1, 2, 3, 1, 2] +``` + +```ts +import { pipe } from "@core/pipe"; +import { cycle } from "@core/iterutil/pipe/async/cycle"; +import { take } from "@core/iterutil/pipe/async/take"; + +const iter = pipe( + [1, 2, 3], + cycle, + take(5), +); +console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2] +``` + ### drop Drops the first `limit` items from the iterable. @@ -143,6 +324,30 @@ const iter = drop([1, 2, 3, 4, 5], 2); console.log(await Array.fromAsync(iter)); // [3, 4, 5] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { drop } from "@core/iterutil/pipe/drop"; + +const iter = pipe( + [1, 2, 3, 4, 5], + drop(2), +); +console.log(Array.from(iter)); // [3, 4, 5] +``` + +```ts +import { pipe } from "@core/pipe"; +import { drop } from "@core/iterutil/pipe/async/drop"; + +const iter = pipe( + [1, 2, 3, 4, 5], + drop(2), +); +console.log(await Array.fromAsync(iter)); // [3, 4, 5] +``` + ### dropWhile Drops elements from the iterable while the predicate returns true. @@ -167,6 +372,30 @@ const iter = dropWhile( console.log(await Array.fromAsync(iter)); // [3, 4, 5] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { dropWhile } from "@core/iterutil/pipe/drop-while"; + +const iter = pipe( + [1, 2, 3, 4, 5], + dropWhile((v) => v < 3), +); +console.log(Array.from(iter)); // [3, 4, 5] +``` + +```ts +import { pipe } from "@core/pipe"; +import { dropWhile } from "@core/iterutil/pipe/async/drop-while"; + +const iter = pipe( + [1, 2, 3, 4, 5], + dropWhile((v) => v < 3), +); +console.log(await Array.fromAsync(iter)); // [3, 4, 5] +``` + ### enumerate Enumerates an iterable. @@ -185,6 +414,24 @@ const iter = enumerate(["a", "b", "c"]); console.log(await Array.fromAsync(iter)); // [[0, "a"], [1, "b"], [2, "c"]] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { enumerate } from "@core/iterutil/pipe/enumerate"; + +const iter = pipe(["a", "b", "c"], enumerate); +console.log(Array.from(iter)); // [[0, "a"], [1, "b"], [2, "c"]] +``` + +```ts +import { pipe } from "@core/pipe"; +import { enumerate } from "@core/iterutil/pipe/async/enumerate"; + +const iter = pipe(["a", "b", "c"], enumerate); +console.log(await Array.fromAsync(iter)); // [[0, "a"], [1, "b"], [2, "c"]] +``` + ### every Returns true if every element in the iterable satisfies the provided testing @@ -204,6 +451,24 @@ console.log(await every([1, 2, 3], (v) => v > 0)); // true console.log(await every([1, 2, 3], (v) => v > 1)); // false ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { every } from "@core/iterutil/pipe/every"; + +console.log(pipe([1, 2, 3], every((v) => v > 0))); // true +console.log(pipe([1, 2, 3], every((v) => v > 1))); // false +``` + +```ts +import { pipe } from "@core/pipe"; +import { every } from "@core/iterutil/pipe/async/every"; + +console.log(await pipe([1, 2, 3], every((v) => v > 0))); // true +console.log(await pipe([1, 2, 3], every((v) => v > 1))); // false +``` + ### filter Filters an iterable based on a function. @@ -228,6 +493,30 @@ const iter = filter( console.log(await Array.fromAsync(iter)); // [2, 4] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { filter } from "@core/iterutil/pipe/filter"; + +const iter = pipe( + [1, 2, 3, 4, 5], + filter((v) => v % 2 === 0), +); +console.log(Array.from(iter)); // [2, 4] +``` + +```ts +import { pipe } from "@core/pipe"; +import { filter } from "@core/iterutil/pipe/async/filter"; + +const iter = pipe( + [1, 2, 3, 4, 5], + filter((v) => v % 2 === 0), +); +console.log(await Array.fromAsync(iter)); // [2, 4] +``` + ### find Returns the first element in the iterable that satisfies the provided testing @@ -253,6 +542,30 @@ const value = await find( console.log(value); // 2 ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { find } from "@core/iterutil/pipe/find"; + +const value = pipe( + [1, 2, 3, 4, 5], + find((v) => v % 2 === 0), +); +console.log(value); // 2 +``` + +```ts +import { pipe } from "@core/pipe"; +import { find } from "@core/iterutil/pipe/async/find"; + +const value = await pipe( + [1, 2, 3, 4, 5], + find((v) => v % 2 === 0), +); +console.log(value); // 2 +``` + ### first Returns the first element of an iterable. If the iterable is empty, returns @@ -272,6 +585,24 @@ const result = await first([1, 2, 3]); console.log(result); // 1 ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { first } from "@core/iterutil/pipe/first"; + +const result = pipe([1, 2, 3], first); +console.log(result); // 1 +``` + +```ts +import { pipe } from "@core/pipe"; +import { first } from "@core/iterutil/pipe/async/first"; + +const result = await pipe([1, 2, 3], first); +console.log(result); // 1 +``` + ### flatMap Maps each value in an iterable to an iterable, then flattens the result. @@ -296,6 +627,30 @@ const iter = flatMap( console.log(await Array.fromAsync(iter)); // [1, 1, 2, 2, 3, 3] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { flatMap } from "@core/iterutil/pipe/flat-map"; + +const iter = pipe( + [1, 2, 3], + flatMap((v) => [v, v]), +); +console.log(Array.from(iter)); // [1, 1, 2, 2, 3, 3] +``` + +```ts +import { pipe } from "@core/pipe"; +import { flatMap } from "@core/iterutil/pipe/async/flat-map"; + +const iter = pipe( + [1, 2, 3], + flatMap((v) => [v, v]), +); +console.log(await Array.fromAsync(iter)); // [1, 1, 2, 2, 3, 3] +``` + ### flatten Flattens an iterable of iterables into a single iterable. @@ -314,6 +669,30 @@ const iter = flatten([[1, 2], [3, 4], [5]]); console.log(await Array.fromAsync(iter)); // [1, 2, 3, 4, 5] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { flatten } from "@core/iterutil/pipe/flatten"; + +const iter = pipe( + [[1, 2], [3, 4], [5]], + flatten, +); +console.log(Array.from(iter)); // [1, 2, 3, 4, 5] +``` + +```ts +import { pipe } from "@core/pipe"; +import { flatten } from "@core/iterutil/pipe/async/flatten"; + +const iter = pipe( + [[1, 2], [3, 4], [5]], + flatten, +); +console.log(await Array.fromAsync(iter)); // [1, 2, 3, 4, 5] +``` + ### forEach Calls a function for each value in an iterable. @@ -334,6 +713,32 @@ await forEach([1, 2, 3], (v) => console.log(v)); // 3 ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { forEach } from "@core/iterutil/pipe/for-each"; +pipe( + [1, 2, 3], + forEach((v) => console.log(v)), +); +// 1 +// 2 +// 3 +``` + +```ts +import { pipe } from "@core/pipe"; +import { forEach } from "@core/iterutil/pipe/async/for-each"; +await pipe( + [1, 2, 3], + forEach((v) => console.log(v)), +); +// 1 +// 2 +// 3 +``` + ### iter Converts an iterable to an iterator. @@ -386,6 +791,24 @@ console.log(await last([1, 2, 3])); // 3 console.log(await last([])); // undefined ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { last } from "@core/iterutil/pipe/last"; + +console.log(pipe([1, 2, 3], last)); // 3 +console.log(pipe([], last)); // undefined +``` + +```ts +import { pipe } from "@core/pipe"; +import { last } from "@core/iterutil/pipe/async/last"; + +console.log(await pipe([1, 2, 3], last)); // 3 +console.log(await pipe([], last)); // undefined +``` + ### map Maps an iterable with a function. @@ -410,6 +833,30 @@ const iter = map( console.log(await Array.fromAsync(iter)); // [2, 4, 6] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { map } from "@core/iterutil/pipe/map"; + +const iter = pipe( + [1, 2, 3], + map((v) => v * 2), +); +console.log(Array.from(iter)); // [2, 4, 6] +``` + +```ts +import { pipe } from "@core/pipe"; +import { map } from "@core/iterutil/pipe/async/map"; + +const iter = pipe( + [1, 2, 3], + map((v) => v * 2), +); +console.log(await Array.fromAsync(iter)); // [2, 4, 6] +``` + ### pairwise Returns an iterable that pairs adjacent elements from the input iterable. @@ -428,6 +875,30 @@ const iter = pairwise([1, 2, 3, 4, 5]); console.log(await Array.fromAsync(iter)); // [[1, 2], [2, 3], [3, 4], [4, 5]] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { pairwise } from "@core/iterutil/pipe/pairwise"; + +const iter = pipe( + [1, 2, 3, 4, 5], + pairwise, +); +console.log(Array.from(iter)); // [[1, 2], [2, 3], [3, 4], [4, 5]] +``` + +```ts +import { pipe } from "@core/pipe"; +import { pairwise } from "@core/iterutil/pipe/async/pairwise"; + +const iter = pipe( + [1, 2, 3, 4, 5], + pairwise, +); +console.log(await Array.fromAsync(iter)); // [[1, 2], [2, 3], [3, 4], [4, 5]] +``` + ### partition Partitions an iterable into two arrays based on a selector function. @@ -454,6 +925,32 @@ console.log(even); // [2, 4] console.log(odd); // [1, 3, 5] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { partition } from "@core/iterutil/pipe/partition"; + +const [even, odd] = pipe( + [1, 2, 3, 4, 5], + partition((v) => v % 2 === 0), +); +console.log(even); // [2, 4] +console.log(odd); // [1, 3, 5] +``` + +```ts +import { pipe } from "@core/pipe"; +import { partition } from "@core/iterutil/pipe/async/partition"; + +const [even, odd] = await pipe( + [1, 2, 3, 4, 5], + partition((v) => v % 2 === 0), +); +console.log(even); // [2, 4] +console.log(odd); // [1, 3, 5] +``` + ### range Generates a range of numbers. @@ -503,6 +1000,42 @@ const joined = await reduce( console.log(joined); // 12345 ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { reduce } from "@core/iterutil/pipe/reduce"; + +const sum = pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v), +); +console.log(sum); // 15 + +const joined = pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v, ""), +); +console.log(joined); // 12345 +``` + +```ts +import { pipe } from "@core/pipe"; +import { reduce } from "@core/iterutil/pipe/async/reduce"; + +const sum = await pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v), +); +console.log(sum); // 15 + +const joined = await pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v, ""), +); +console.log(joined); // 12345 +``` + ### some Returns true if at least one element in the iterable satisfies the provided @@ -521,6 +1054,24 @@ console.log(await some([1, 2, 3], (v) => v % 2 === 0)); // true console.log(await some([1, 3, 5], (v) => v % 2 === 0)); // false ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { some } from "@core/iterutil/pipe/some"; + +console.log(pipe([1, 2, 3], some((v) => v % 2 === 0))); // true +console.log(pipe([1, 3, 5], some((v) => v % 2 === 0))); // false +``` + +```ts +import { pipe } from "@core/pipe"; +import { some } from "@core/iterutil/pipe/async/some"; + +console.log(await pipe([1, 2, 3], some((v) => v % 2 === 0))); // true +console.log(await pipe([1, 3, 5], some((v) => v % 2 === 0))); // false +``` + ### take Takes the first `limit` items from the iterable. @@ -539,6 +1090,30 @@ const iter = take([1, 2, 3, 4, 5], 2); console.log(await Array.fromAsync(iter)); // [1, 2] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { take } from "@core/iterutil/pipe/take"; + +const iter = pipe( + [1, 2, 3, 4, 5], + take(2), +); +console.log(Array.from(iter)); // [1, 2] +``` + +```ts +import { pipe } from "@core/pipe"; +import { take } from "@core/iterutil/pipe/async/take"; + +const iter = pipe( + [1, 2, 3, 4, 5], + take(2), +); +console.log(await Array.fromAsync(iter)); // [1, 2] +``` + ### takeWhile Takes elements from the iterable while the predicate is true. @@ -563,6 +1138,30 @@ const iter = takeWhile( console.log(await Array.fromAsync(iter)); // [1, 2, 3] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { takeWhile } from "@core/iterutil/pipe/take-while"; + +const iter = pipe( + [1, 2, 3, 4, 5], + takeWhile((v) => v < 4), +); +console.log(Array.from(iter)); // [1, 2, 3] +``` + +```ts +import { pipe } from "@core/pipe"; +import { takeWhile } from "@core/iterutil/pipe/async/take-while"; + +const iter = pipe( + [1, 2, 3, 4, 5], + takeWhile((v) => v < 4), +); +console.log(await Array.fromAsync(iter)); // [1, 2, 3] +``` + ### toAsyncIterable Converts an iterable to an async iterable. @@ -604,6 +1203,42 @@ const iter2 = uniq( console.log(await Array.fromAsync(iter2)); // [1, 2, 3, 4] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { uniq } from "@core/iterutil/pipe/uniq"; + +const iter1 = pipe( + [1, 2, 2, 3, 3, 3], + uniq(), +); +console.log(Array.from(iter1)); // [1, 2, 3] + +const iter2 = pipe( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + uniq((v) => v % 4), +); +console.log(Array.from(iter2)); // [1, 2, 3, 4] +``` + +```ts +import { pipe } from "@core/pipe"; +import { uniq } from "@core/iterutil/pipe/async/uniq"; + +const iter1 = pipe( + [1, 2, 2, 3, 3, 3], + uniq(), +); +console.log(await Array.fromAsync(iter1)); // [1, 2, 3] + +const iter2 = pipe( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + uniq((v) => v % 4), +); +console.log(await Array.fromAsync(iter2)); // [1, 2, 3, 4] +``` + ### zip Zips multiple iterables into a single iterable. @@ -622,6 +1257,30 @@ const iter = zip([1, 2, 3], ["a", "b", "c"]); console.log(await Array.fromAsync(iter)); // [[1, "a"], [2, "b"], [3, "c"]] ``` +Use `pipe` and `pipe/async` modules for [@core/pipe] package like. + +```ts +import { pipe } from "@core/pipe"; +import { zip } from "@core/iterutil/pipe/zip"; + +const iter = pipe( + [1, 2, 3], + zip(["a", "b", "c"]), +); +console.log(Array.from(iter)); // [[1, "a"], [2, "b"], [3, "c"]] +``` + +```ts +import { pipe } from "@core/pipe"; +import { zip } from "@core/iterutil/pipe/async/zip"; + +const iter = pipe( + [1, 2, 3], + zip(["a", "b", "c"]), +); +console.log(await Array.fromAsync(iter)); // [[1, "a"], [2, "b"], [3, "c"]] +``` + ## License The code follows MIT license written in [LICENSE](./LICENSE). Contributors need diff --git a/README_test.ts b/README_test.ts new file mode 100644 index 0000000..52adbd6 --- /dev/null +++ b/README_test.ts @@ -0,0 +1,40 @@ +import { assertEquals } from "@std/assert"; +import { pipe } from "@core/pipe"; +import * as rootMod from "@core/iterutil"; +import * as asyncMod from "@core/iterutil/async"; +import * as pipeMod from "@core/iterutil/pipe"; +import * as pipeAsyncMod from "@core/iterutil/pipe/async"; + +Deno.test("README", async (t) => { + await t.step("case 1", () => { + const iter = rootMod.map([1, 2, 3], (v) => v * 2); + assertEquals(Array.from(iter), [2, 4, 6]); + }); + + await t.step("case 2", async () => { + const iter = asyncMod.map([1, 2, 3], (v) => Promise.resolve(v * 2)); + assertEquals(await Array.fromAsync(iter), [2, 4, 6]); + }); + + await t.step("case 3", () => { + const iter = pipe( + [1, 2, 3], + pipeMod.map((v) => v * 2), + pipeMod.cycle, + pipeMod.take(10), + pipeMod.filter((v) => v % 2 === 0), + ); + assertEquals(Array.from(iter), [2, 4, 6, 2, 4, 6, 2, 4, 6, 2]); + }); + + await t.step("case 4", async () => { + const iter = pipe( + [1, 2, 3], + pipeAsyncMod.map((v) => v * 2), + pipeAsyncMod.cycle, + pipeAsyncMod.take(10), + pipeAsyncMod.filter((v) => v % 2 === 0), + ); + assertEquals(await Array.fromAsync(iter), [2, 4, 6, 2, 4, 6, 2, 4, 6, 2]); + }); +}); diff --git a/async/uniq.ts b/async/uniq.ts index 0431a54..fc7a386 100644 --- a/async/uniq.ts +++ b/async/uniq.ts @@ -1,6 +1,8 @@ /** * Returns an iterable that yields the unique elements of the input iterable. * + * Use {@linkcode https://jsr.io/@core/iterutil/doc/uniq/~/uniq uniq} to get the unique elements synchronously. + * * @param iterable The iterable to get the unique elements of. * @param identify An optional function to transform the elements before checking for uniqueness. * @returns An iterable that yields the unique elements of the input iterable. diff --git a/deno.jsonc b/deno.jsonc index 5af50ed..bfba179 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -52,6 +52,58 @@ "./map": "./map.ts", "./pairwise": "./pairwise.ts", "./partition": "./partition.ts", + "./pipe": "./pipe/mod.ts", + "./pipe/async": "./pipe/async/mod.ts", + "./pipe/async/chain": "./pipe/async/chain.ts", + "./pipe/async/chunked": "./pipe/async/chunked.ts", + "./pipe/async/compact": "./pipe/async/compact.ts", + "./pipe/async/compress": "./pipe/async/compress.ts", + "./pipe/async/cycle": "./pipe/async/cycle.ts", + "./pipe/async/drop": "./pipe/async/drop.ts", + "./pipe/async/drop-while": "./pipe/async/drop_while.ts", + "./pipe/async/enumerate": "./pipe/async/enumerate.ts", + "./pipe/async/every": "./pipe/async/every.ts", + "./pipe/async/filter": "./pipe/async/filter.ts", + "./pipe/async/find": "./pipe/async/find.ts", + "./pipe/async/first": "./pipe/async/first.ts", + "./pipe/async/flat-map": "./pipe/async/flat_map.ts", + "./pipe/async/flatten": "./pipe/async/flatten.ts", + "./pipe/async/for-each": "./pipe/async/for_each.ts", + "./pipe/async/last": "./pipe/async/last.ts", + "./pipe/async/map": "./pipe/async/map.ts", + "./pipe/async/pairwise": "./pipe/async/pairwise.ts", + "./pipe/async/partition": "./pipe/async/partition.ts", + "./pipe/async/reduce": "./pipe/async/reduce.ts", + "./pipe/async/some": "./pipe/async/some.ts", + "./pipe/async/take": "./pipe/async/take.ts", + "./pipe/async/take-while": "./pipe/async/take_while.ts", + "./pipe/async/uniq": "./pipe/async/uniq.ts", + "./pipe/async/zip": "./pipe/async/zip.ts", + "./pipe/chain": "./pipe/chain.ts", + "./pipe/chunked": "./pipe/chunked.ts", + "./pipe/compact": "./pipe/compact.ts", + "./pipe/compress": "./pipe/compress.ts", + "./pipe/cycle": "./pipe/cycle.ts", + "./pipe/drop": "./pipe/drop.ts", + "./pipe/drop-while": "./pipe/drop_while.ts", + "./pipe/enumerate": "./pipe/enumerate.ts", + "./pipe/every": "./pipe/every.ts", + "./pipe/filter": "./pipe/filter.ts", + "./pipe/find": "./pipe/find.ts", + "./pipe/first": "./pipe/first.ts", + "./pipe/flat-map": "./pipe/flat_map.ts", + "./pipe/flatten": "./pipe/flatten.ts", + "./pipe/for-each": "./pipe/for_each.ts", + "./pipe/last": "./pipe/last.ts", + "./pipe/map": "./pipe/map.ts", + "./pipe/pairwise": "./pipe/pairwise.ts", + "./pipe/partition": "./pipe/partition.ts", + "./pipe/reduce": "./pipe/reduce.ts", + "./pipe/some": "./pipe/some.ts", + "./pipe/take": "./pipe/take.ts", + "./pipe/take-while": "./pipe/take_while.ts", + "./pipe/uniq": "./pipe/uniq.ts", + "./pipe/zip": "./pipe/zip.ts", "./range": "./range.ts", "./reduce": "./reduce.ts", "./some": "./some.ts", @@ -126,6 +178,62 @@ "@core/iterutil/map": "./map.ts", "@core/iterutil/pairwise": "./pairwise.ts", "@core/iterutil/partition": "./partition.ts", + "@core/iterutil/pipe": "./pipe/mod.ts", + "@core/iterutil/pipe/async": "./pipe/async/mod.ts", + "@core/iterutil/pipe/async/chain": "./pipe/async/chain.ts", + "@core/iterutil/pipe/async/chunked": "./pipe/async/chunked.ts", + "@core/iterutil/pipe/async/compact": "./pipe/async/compact.ts", + "@core/iterutil/pipe/async/compress": "./pipe/async/compress.ts", + "@core/iterutil/pipe/async/count": "./pipe/async/count.ts", + "@core/iterutil/pipe/async/cycle": "./pipe/async/cycle.ts", + "@core/iterutil/pipe/async/drop": "./pipe/async/drop.ts", + "@core/iterutil/pipe/async/drop-while": "./pipe/async/drop_while.ts", + "@core/iterutil/pipe/async/enumerate": "./pipe/async/enumerate.ts", + "@core/iterutil/pipe/async/every": "./pipe/async/every.ts", + "@core/iterutil/pipe/async/filter": "./pipe/async/filter.ts", + "@core/iterutil/pipe/async/find": "./pipe/async/find.ts", + "@core/iterutil/pipe/async/first": "./pipe/async/first.ts", + "@core/iterutil/pipe/async/flat-map": "./pipe/async/flat_map.ts", + "@core/iterutil/pipe/async/flatten": "./pipe/async/flatten.ts", + "@core/iterutil/pipe/async/for-each": "./pipe/async/for_each.ts", + "@core/iterutil/pipe/async/iter": "./pipe/async/iter.ts", + "@core/iterutil/pipe/async/last": "./pipe/async/last.ts", + "@core/iterutil/pipe/async/map": "./pipe/async/map.ts", + "@core/iterutil/pipe/async/pairwise": "./pipe/async/pairwise.ts", + "@core/iterutil/pipe/async/partition": "./pipe/async/partition.ts", + "@core/iterutil/pipe/async/reduce": "./pipe/async/reduce.ts", + "@core/iterutil/pipe/async/some": "./pipe/async/some.ts", + "@core/iterutil/pipe/async/take": "./pipe/async/take.ts", + "@core/iterutil/pipe/async/take-while": "./pipe/async/take_while.ts", + "@core/iterutil/pipe/async/uniq": "./pipe/async/uniq.ts", + "@core/iterutil/pipe/async/zip": "./pipe/async/zip.ts", + "@core/iterutil/pipe/chain": "./pipe/chain.ts", + "@core/iterutil/pipe/chunked": "./pipe/chunked.ts", + "@core/iterutil/pipe/compact": "./pipe/compact.ts", + "@core/iterutil/pipe/compress": "./pipe/compress.ts", + "@core/iterutil/pipe/count": "./pipe/count.ts", + "@core/iterutil/pipe/cycle": "./pipe/cycle.ts", + "@core/iterutil/pipe/drop": "./pipe/drop.ts", + "@core/iterutil/pipe/drop-while": "./pipe/drop_while.ts", + "@core/iterutil/pipe/enumerate": "./pipe/enumerate.ts", + "@core/iterutil/pipe/every": "./pipe/every.ts", + "@core/iterutil/pipe/filter": "./pipe/filter.ts", + "@core/iterutil/pipe/find": "./pipe/find.ts", + "@core/iterutil/pipe/first": "./pipe/first.ts", + "@core/iterutil/pipe/flat-map": "./pipe/flat_map.ts", + "@core/iterutil/pipe/flatten": "./pipe/flatten.ts", + "@core/iterutil/pipe/for-each": "./pipe/for_each.ts", + "@core/iterutil/pipe/iter": "./pipe/iter.ts", + "@core/iterutil/pipe/last": "./pipe/last.ts", + "@core/iterutil/pipe/map": "./pipe/map.ts", + "@core/iterutil/pipe/pairwise": "./pipe/pairwise.ts", + "@core/iterutil/pipe/partition": "./pipe/partition.ts", + "@core/iterutil/pipe/reduce": "./pipe/reduce.ts", + "@core/iterutil/pipe/some": "./pipe/some.ts", + "@core/iterutil/pipe/take": "./pipe/take.ts", + "@core/iterutil/pipe/take-while": "./pipe/take_while.ts", + "@core/iterutil/pipe/uniq": "./pipe/uniq.ts", + "@core/iterutil/pipe/zip": "./pipe/zip.ts", "@core/iterutil/range": "./range.ts", "@core/iterutil/reduce": "./reduce.ts", "@core/iterutil/some": "./some.ts", @@ -133,6 +241,7 @@ "@core/iterutil/take-while": "./take_while.ts", "@core/iterutil/uniq": "./uniq.ts", "@core/iterutil/zip": "./zip.ts", + "@core/pipe": "jsr:@core/pipe@^0.2.0", "@core/unknownutil": "jsr:@core/unknownutil@^4.0.1", "@std/assert": "jsr:@std/assert@^1.0.2", "@std/jsonc": "jsr:@std/jsonc@^1.0.0", diff --git a/pipe/async/chain.ts b/pipe/async/chain.ts new file mode 100644 index 0000000..fb5a959 --- /dev/null +++ b/pipe/async/chain.ts @@ -0,0 +1,35 @@ +import { type Chain, chain as base } from "@core/iterutil/async/chain"; + +/** + * Returns an operator that chains multiple iterables to the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/chain/~/chain chain} for native chain. + * + * @param iterables The iterables to chain to the iterable. + * @returns An operator that chains multiple iterables to the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { chain } from "@core/iterutil/pipe/async/chain"; + * + * const iter = pipe( + * [1, 2, 3], + * chain(["a", "b"], [true]), + * ); + * console.log(await Array.fromAsync(iter)); // [1, 2, 3, "a", "b", true] + * ``` + */ +export function chain< + U extends readonly [ + Iterable | AsyncIterable, + ...(Iterable | AsyncIterable)[], + ], +>( + ...iterables: U +): ( + iterable: Iterable | AsyncIterable, +) => AsyncIterable> { + return (iterable: Iterable | AsyncIterable) => + base(iterable, ...iterables) as AsyncIterable>; +} diff --git a/pipe/async/chain_test.ts b/pipe/async/chain_test.ts new file mode 100644 index 0000000..87f07ae --- /dev/null +++ b/pipe/async/chain_test.ts @@ -0,0 +1,20 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { chain } from "./chain.ts"; + +Deno.test("chain", async (t) => { + await t.step("usage", async () => { + const result = pipe( + [1, 2, 3], + chain(["a", "b"], [true]), + ); + const expected = [1, 2, 3, "a", "b", true]; + assertEquals(await Array.fromAsync(result), expected); + assertType< + IsExact> + >( + true, + ); + }); +}); diff --git a/pipe/async/chunked.ts b/pipe/async/chunked.ts new file mode 100644 index 0000000..05a9a2b --- /dev/null +++ b/pipe/async/chunked.ts @@ -0,0 +1,27 @@ +import { chunked as base } from "@core/iterutil/async/chunked"; + +/** + * Returns an operator that chunks the iterable into arrays of `size`. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/chunked/~/chunked chunked} for native chunked. + * + * @param size The size of each chunk. + * @return An operator that chunks the iterable into arrays of `size`. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { chunked } from "@core/iterutil/pipe/async/chunked"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * chunked(2), + * ); + * console.log(await Array.fromAsync(iter)); // [[1, 2], [3, 4], [5]] + * ``` + */ +export function chunked( + size: number, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, size); +} diff --git a/pipe/async/chunked_test.ts b/pipe/async/chunked_test.ts new file mode 100644 index 0000000..e1c8a42 --- /dev/null +++ b/pipe/async/chunked_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { chunked } from "./chunked.ts"; + +Deno.test("chunked", async (t) => { + await t.step("usage", async () => { + const result = pipe([1, 2, 3, 4, 5, 6], chunked(2)); + const expected = [[1, 2], [3, 4], [5, 6]]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/compact.ts b/pipe/async/compact.ts new file mode 100644 index 0000000..c948c8f --- /dev/null +++ b/pipe/async/compact.ts @@ -0,0 +1,22 @@ +import { compact } from "@core/iterutil/async/compact"; + +export { + /** + * An operator to remove all nullish (`null` or `undefined`) values from an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/compact/~/compact compact} for native compact. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { compact } from "@core/iterutil/pipe/async/compact"; + * + * const iter = pipe( + * [1, undefined, 2, null, 3], + * compact, + * ); + * console.log(await Array.fromAsync(iter)); // [1, 2, 3] + * ``` + */ + compact, +}; diff --git a/pipe/async/compact_test.ts b/pipe/async/compact_test.ts new file mode 100644 index 0000000..0745363 --- /dev/null +++ b/pipe/async/compact_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { compact } from "./compact.ts"; + +Deno.test("compact", async (t) => { + await t.step("usage", async () => { + const result = pipe([1, undefined, 2, null, 3], compact); + const expected = [1, 2, 3]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/compress.ts b/pipe/async/compress.ts new file mode 100644 index 0000000..65a3261 --- /dev/null +++ b/pipe/async/compress.ts @@ -0,0 +1,27 @@ +import { compress as base } from "@core/iterutil/async/compress"; + +/** + * Returns an operator that compresses an iterable by selecting elements using a selector iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/compress/~/compress compress} for native compress. + * + * @param selectors The selectors to use. + * @returns An operator that compresses an iterable by selecting elements using a selector iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { compress } from "@core/iterutil/pipe/async/compress"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * compress([true, false, true, false, true]), + * ); + * console.log(await Array.fromAsync(iter)); // [1, 3, 5] + * ``` + */ +export function compress( + selectors: Iterable | AsyncIterable, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, selectors); +} diff --git a/pipe/async/compress_test.ts b/pipe/async/compress_test.ts new file mode 100644 index 0000000..3b800bd --- /dev/null +++ b/pipe/async/compress_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { compress } from "./compress.ts"; + +Deno.test("compress", async (t) => { + await t.step("usage", async () => { + const result = pipe( + [1, 2, 3, 4, 5], + compress([true, false, true, false, true]), + ); + const expected = [1, 3, 5]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/cycle.ts b/pipe/async/cycle.ts new file mode 100644 index 0000000..889f99c --- /dev/null +++ b/pipe/async/cycle.ts @@ -0,0 +1,24 @@ +import { cycle } from "@core/iterutil/async/cycle"; + +export { + /** + * An operator to return a function that cycles the elements of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/cycle/~/cycle cycle} for native cycle. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { cycle } from "@core/iterutil/pipe/async/cycle"; + * import { take } from "@core/iterutil/pipe/async/take"; + * + * const iter = pipe( + * [1, 2, 3], + * cycle, + * take(5), + * ); + * console.log(await Array.fromAsync(iter)); // [1, 2, 3, 1, 2] + * ``` + */ + cycle, +}; diff --git a/pipe/async/cycle_test.ts b/pipe/async/cycle_test.ts new file mode 100644 index 0000000..5555703 --- /dev/null +++ b/pipe/async/cycle_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { take } from "./take.ts"; +import { cycle } from "./cycle.ts"; + +Deno.test("cycle", async (t) => { + await t.step("usage", async () => { + const result = pipe([0, 1, 2], cycle, take(5)); + const expected = [0, 1, 2, 0, 1]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/drop.ts b/pipe/async/drop.ts new file mode 100644 index 0000000..e479b19 --- /dev/null +++ b/pipe/async/drop.ts @@ -0,0 +1,28 @@ +import { drop as base } from "@core/iterutil/async/drop"; + +/** + * Returns an operator that drops the first `limit` items from the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/drop/~/drop drop} for native drop. + * + * @param limit The number of items to drop. It must be 0 or positive safe integer. + * @returns An operator that drops the first `limit` items from the iterable. + * @throws {RangeError} if `limit` is less than 0 or non safe integer. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { drop } from "@core/iterutil/pipe/async/drop"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * drop(2), + * ); + * console.log(await Array.fromAsync(iter)); // [3, 4, 5] + * ``` + */ +export function drop( + limit: number, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, limit); +} diff --git a/pipe/async/drop_test.ts b/pipe/async/drop_test.ts new file mode 100644 index 0000000..da1b68e --- /dev/null +++ b/pipe/async/drop_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { drop } from "./drop.ts"; + +Deno.test("drop", async (t) => { + await t.step("usage", async () => { + const result = pipe([0, 1, 2, 3, 4], drop(2)); + const expected = [2, 3, 4]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/drop_while.ts b/pipe/async/drop_while.ts new file mode 100644 index 0000000..c82534a --- /dev/null +++ b/pipe/async/drop_while.ts @@ -0,0 +1,27 @@ +import { dropWhile as base } from "@core/iterutil/async/drop-while"; + +/** + * Returns an operator that drops elements from the iterable while the predicate returns true. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/drop-while/~/dropWhile dropWhile} for native dropWhile. + * + * @param fn The predicate function. + * @returns An operator that drops elements from the iterable while the predicate returns true. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { dropWhile } from "@core/iterutil/pipe/async/drop-while"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * dropWhile((v) => v < 3), + * ); + * console.log(await Array.fromAsync(iter)); // [3, 4, 5] + * ``` + */ +export function dropWhile( + fn: (value: T) => boolean, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/drop_while_test.ts b/pipe/async/drop_while_test.ts new file mode 100644 index 0000000..90dec97 --- /dev/null +++ b/pipe/async/drop_while_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { dropWhile } from "./drop_while.ts"; + +Deno.test("dropWhile", async (t) => { + await t.step("usage", async () => { + const result = pipe( + [0, 1, 2, 3, 4], + dropWhile((v) => v < 2), + ); + const expected = [2, 3, 4]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/enumerate.ts b/pipe/async/enumerate.ts new file mode 100644 index 0000000..6135971 --- /dev/null +++ b/pipe/async/enumerate.ts @@ -0,0 +1,41 @@ +import { enumerate as base } from "@core/iterutil/async/enumerate"; + +/** + * Returns an operator that enumerates the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/enumerate/~/enumerate enumerate} for native enumerate. + * + * @param start The starting index. + * @param step The step between indices. + * @returns An operator that enumerates the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { enumerate } from "@core/iterutil/pipe/async/enumerate"; + * + * const iter1 = pipe( + * ["a", "b", "c"], + * enumerate(), + * ); + * console.log(await Array.fromAsync(iter1)); // [[0, "a"], [1, "b"], [2, "c"]] + * + * const iter2 = pipe( + * ["a", "b", "c"], + * enumerate(1), + * ); + * console.log(await Array.fromAsync(iter2)); // [[1, "a"], [2, "b"], [3, "c"]] + * + * const iter3 = pipe( + * ["a", "b", "c"], + * enumerate(1, 2), + * ); + * console.log(await Array.fromAsync(iter3)); // [[1, "a"], [3, "b"], [5, "c"]] + * ``` + */ +export function enumerate( + start: number = 0, + step: number = 1, +): (iterable: Iterable | AsyncIterable) => AsyncIterable<[number, T]> { + return (iterable) => base(iterable, start, step); +} diff --git a/pipe/async/enumerate_test.ts b/pipe/async/enumerate_test.ts new file mode 100644 index 0000000..bb733fb --- /dev/null +++ b/pipe/async/enumerate_test.ts @@ -0,0 +1,27 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { enumerate } from "./enumerate.ts"; + +Deno.test("enumerate", async (t) => { + await t.step("usage 1", async () => { + const result = pipe(["a", "b", "c"], enumerate()); + const expected = [[0, "a"], [1, "b"], [2, "c"]]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); + + await t.step("usage 2", async () => { + const result = pipe(["a", "b", "c"], enumerate(1)); + const expected = [[1, "a"], [2, "b"], [3, "c"]]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); + + await t.step("usage 3", async () => { + const result = pipe(["a", "b", "c"], enumerate(1, 2)); + const expected = [[1, "a"], [3, "b"], [5, "c"]]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/every.ts b/pipe/async/every.ts new file mode 100644 index 0000000..fb73d7d --- /dev/null +++ b/pipe/async/every.ts @@ -0,0 +1,27 @@ +import { every as base } from "@core/iterutil/async/every"; + +/** + * Returns an operator that tests whether every element in the iterable satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/every/~/every every} for native every. + * + * @param fn The testing function. + * @returns A function that tests whether every element in the iterable satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { every } from "@core/iterutil/pipe/async/every"; + * + * const value = await pipe( + * [1, 2, 3], + * every((v) => v > 0), + * ); + * console.log(value); // true + * ``` + */ +export function every( + fn: (value: T) => boolean, +): (iterable: Iterable | AsyncIterable) => Promise { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/every_test.ts b/pipe/async/every_test.ts new file mode 100644 index 0000000..f370c9f --- /dev/null +++ b/pipe/async/every_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { every } from "./every.ts"; + +Deno.test("every", async (t) => { + await t.step("usage", async () => { + const result = await pipe([1, 2, 3], every((v) => v > 0)); + const expected = true; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/async/filter.ts b/pipe/async/filter.ts new file mode 100644 index 0000000..0bce187 --- /dev/null +++ b/pipe/async/filter.ts @@ -0,0 +1,27 @@ +import { filter as base } from "@core/iterutil/async/filter"; + +/** + * Returns an operator that filters an iterable based on a function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/filter/~/filter filter} for native filter. + * + * @params fn The function to filter the iterable. + * @returns An operator that filters an iterable based on a function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { filter } from "@core/iterutil/pipe/async/filter"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * filter((v) => v % 2 === 0), + * ); + * console.log(await Array.fromAsync(iter)); // [2, 4] + * ``` + */ +export function filter( + fn: (value: T, index: number) => boolean | Promise, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/filter_test.ts b/pipe/async/filter_test.ts new file mode 100644 index 0000000..8498e72 --- /dev/null +++ b/pipe/async/filter_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { filter } from "./filter.ts"; + +Deno.test("filter", async () => { + const result = pipe( + [1, 2, 3, 4, 5], + filter((v) => v % 2 === 0), + ); + const expected = [2, 4]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); +}); diff --git a/pipe/async/find.ts b/pipe/async/find.ts new file mode 100644 index 0000000..f2ab497 --- /dev/null +++ b/pipe/async/find.ts @@ -0,0 +1,27 @@ +import { find as base } from "@core/iterutil/async/find"; + +/** + * Returns an operator that finds the first element that satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/find/~/find find} for native find. + * + * @param fn The testing function. + * @returns An operator that finds the first element that satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { find } from "@core/iterutil/pipe/async/find"; + * + * const value = await pipe( + * [1, 2, 3, 4, 5], + * find((v) => v % 2 === 0), + * ); + * console.log(value); // 2 + * ``` + */ +export function find( + fn: (value: T, index: number) => boolean | Promise, +): (iterable: Iterable | AsyncIterable) => Promise { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/find_test.ts b/pipe/async/find_test.ts new file mode 100644 index 0000000..4f6b09a --- /dev/null +++ b/pipe/async/find_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { find } from "./find.ts"; + +Deno.test("find", async (t) => { + await t.step("usage", async () => { + const result = await pipe( + [1, 2, 3, 4, 5], + find((v) => v % 2 === 0), + ); + const expected = 2; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/async/first.ts b/pipe/async/first.ts new file mode 100644 index 0000000..0ccf5b7 --- /dev/null +++ b/pipe/async/first.ts @@ -0,0 +1,22 @@ +import { first } from "@core/iterutil/async/first"; + +export { + /** + * An operator that gets the first element of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/first/~/first first} for native first. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { first } from "@core/iterutil/pipe/async/first"; + * + * const value = await pipe( + * [1, 2, 3], + * first, + * ); + * console.log(value); // 1 + * ``` + */ + first, +}; diff --git a/pipe/async/first_test.ts b/pipe/async/first_test.ts new file mode 100644 index 0000000..698d07f --- /dev/null +++ b/pipe/async/first_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { first } from "./first.ts"; + +Deno.test("first", async (t) => { + await t.step("usage", async () => { + const result = await pipe([1, 2, 3], first); + const expected = 1; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/async/flat_map.ts b/pipe/async/flat_map.ts new file mode 100644 index 0000000..2c683ac --- /dev/null +++ b/pipe/async/flat_map.ts @@ -0,0 +1,30 @@ +import { flatMap as base } from "@core/iterutil/async/flat-map"; + +/** + * Returns an operator that flat maps the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/flat-map/~/flatMap flatMap} for native flatMap. + * + * @param fn The flat mapping function. + * @returns An operator that flat maps the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { flatMap } from "@core/iterutil/pipe/async/flat-map"; + * + * const iter = pipe( + * [1, 2, 3], + * flatMap((v) => [v, v]), + * ); + * console.log(await Array.fromAsync(iter)); // [1, 1, 2, 2, 3, 3] + * ``` + */ +export function flatMap( + fn: ( + value: T, + index: number, + ) => Iterable | AsyncIterable | Promise>, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/flat_map_test.ts b/pipe/async/flat_map_test.ts new file mode 100644 index 0000000..d7254ea --- /dev/null +++ b/pipe/async/flat_map_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { flatMap } from "./flat_map.ts"; + +Deno.test("flatMap", async (t) => { + await t.step("usage", async () => { + const result = pipe( + [1, 2, 3], + flatMap((v) => [v, v]), + ); + const expected = [1, 1, 2, 2, 3, 3]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/flatten.ts b/pipe/async/flatten.ts new file mode 100644 index 0000000..03735fb --- /dev/null +++ b/pipe/async/flatten.ts @@ -0,0 +1,22 @@ +import { flatten } from "@core/iterutil/async/flatten"; + +export { + /** + * An operator that flattens an iterable of iterables. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/flatten/~/flatten flatten} for native flatten. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { flatten } from "@core/iterutil/pipe/async/flatten"; + * + * const iter = pipe( + * [[1, 2], [3, 4], [5]], + * flatten, + * ); + * console.log(await Array.fromAsync(iter)); // [1, 2, 3, 4, 5] + * ``` + */ + flatten, +}; diff --git a/pipe/async/flatten_test.ts b/pipe/async/flatten_test.ts new file mode 100644 index 0000000..8f0520e --- /dev/null +++ b/pipe/async/flatten_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { flatten } from "./flatten.ts"; + +Deno.test("flatten", async (t) => { + await t.step("usage", async () => { + const result = pipe([[1, 2], [3, 4], [5]], flatten); + const expected = [1, 2, 3, 4, 5]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/for_each.ts b/pipe/async/for_each.ts new file mode 100644 index 0000000..71a3194 --- /dev/null +++ b/pipe/async/for_each.ts @@ -0,0 +1,29 @@ +import { forEach as base } from "@core/iterutil/async/for-each"; + +/** + * Returns an operator that calls the given function for each value in the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/for-each/~/forEach forEach} for native forEach. + * + * @param fn The function to call for each value. + * @returns An operator that calls the given function for each value in the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { forEach } from "@core/iterutil/pipe/async/for-each"; + * + * await pipe( + * [1, 2, 3], + * forEach((v) => console.log(v)), + * ); + * // 1 + * // 2 + * // 3 + * ``` + */ +export function forEach( + fn: (value: T, index: number) => void | Promise, +): (iterable: Iterable | AsyncIterable) => Promise { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/for_each_test.ts b/pipe/async/for_each_test.ts new file mode 100644 index 0000000..54f4674 --- /dev/null +++ b/pipe/async/for_each_test.ts @@ -0,0 +1,12 @@ +import { assertEquals } from "@std/assert"; +import { pipe } from "@core/pipe"; +import { forEach } from "./for_each.ts"; + +Deno.test("forEach", async () => { + const values: number[] = []; + await pipe( + [1, 2, 3, 4, 5], + forEach((v) => void values.push(v)), + ); + assertEquals(values, [1, 2, 3, 4, 5]); +}); diff --git a/pipe/async/last.ts b/pipe/async/last.ts new file mode 100644 index 0000000..558aa0a --- /dev/null +++ b/pipe/async/last.ts @@ -0,0 +1,22 @@ +import { last } from "@core/iterutil/async/last"; + +export { + /** + * An operator that gets the last element of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/last/~/last last} for native last. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { last } from "@core/iterutil/pipe/async/last"; + * + * const value = await pipe( + * [1, 2, 3], + * last + * ); + * console.log(value); // 3 + * ``` + */ + last, +}; diff --git a/pipe/async/last_test.ts b/pipe/async/last_test.ts new file mode 100644 index 0000000..dec0ef2 --- /dev/null +++ b/pipe/async/last_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { last } from "./last.ts"; + +Deno.test("last", async (t) => { + await t.step("usage", async () => { + const result = await pipe([1, 2, 3], last); + const expected = 3; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/async/map.ts b/pipe/async/map.ts new file mode 100644 index 0000000..6e3f179 --- /dev/null +++ b/pipe/async/map.ts @@ -0,0 +1,27 @@ +import { map as base } from "@core/iterutil/async/map"; + +/** + * Returns an operator that maps the iterable using the provided function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/map/~/map map} for native map. + * + * @param fn The mapping function. + * @returns An operator that maps the iterable using the provided function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { map } from "@core/iterutil/pipe/async/map"; + * + * const iter = pipe( + * [1, 2, 3], + * map((v) => v * 2), + * ); + * console.log(await Array.fromAsync(iter)); // [2, 4, 6] + * ``` + */ +export function map( + fn: (value: T, index: number) => U | Promise, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/map_test.ts b/pipe/async/map_test.ts new file mode 100644 index 0000000..9f3c730 --- /dev/null +++ b/pipe/async/map_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { map } from "./map.ts"; + +Deno.test("map", async () => { + const result = pipe( + [1, 2, 3, 4, 5], + map((v) => v * 2), + ); + const expected = [2, 4, 6, 8, 10]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); +}); diff --git a/pipe/async/mod.ts b/pipe/async/mod.ts new file mode 100644 index 0000000..ea79330 --- /dev/null +++ b/pipe/async/mod.ts @@ -0,0 +1,25 @@ +export * from "./chain.ts"; +export * from "./chunked.ts"; +export * from "./compact.ts"; +export * from "./compress.ts"; +export * from "./cycle.ts"; +export * from "./drop.ts"; +export * from "./drop_while.ts"; +export * from "./enumerate.ts"; +export * from "./every.ts"; +export * from "./filter.ts"; +export * from "./find.ts"; +export * from "./first.ts"; +export * from "./flat_map.ts"; +export * from "./flatten.ts"; +export * from "./for_each.ts"; +export * from "./last.ts"; +export * from "./map.ts"; +export * from "./pairwise.ts"; +export * from "./partition.ts"; +export * from "./reduce.ts"; +export * from "./some.ts"; +export * from "./take.ts"; +export * from "./take_while.ts"; +export * from "./uniq.ts"; +export * from "./zip.ts"; diff --git a/pipe/async/mod_test.ts b/pipe/async/mod_test.ts new file mode 100644 index 0000000..1324a57 --- /dev/null +++ b/pipe/async/mod_test.ts @@ -0,0 +1,59 @@ +import { assertArrayIncludes } from "@std/assert"; +import { basename, globToRegExp, join } from "@std/path"; +import { ensure, is } from "@core/unknownutil"; +import { parse } from "@std/jsonc"; + +const excludes = [ + "mod.ts", + "*_test.ts", + "*_bench.ts", +]; + +Deno.test("mod.ts must exports all exports in public modules", async () => { + const modExports = await listModExports("./mod.ts"); + const pubExports = []; + for await (const name of iterPublicModules(".")) { + pubExports.push(...await listModExports(`./${name}.ts`)); + } + assertArrayIncludes(modExports, pubExports); +}); + +Deno.test("JSR exports must have all exports in mod.ts", async () => { + const jsrExportEntries = await listJsrExportEntries(); + const modExportEntries: [string, string][] = []; + for await (const name of iterPublicModules(".")) { + modExportEntries.push([ + `./pipe/async/${name.replaceAll("_", "-")}`, + `./pipe/async/${name}.ts`, + ]); + } + assertArrayIncludes(jsrExportEntries, modExportEntries); +}); + +async function* iterPublicModules(relpath: string): AsyncIterable { + const patterns = excludes.map((p) => globToRegExp(p)); + const root = join(import.meta.dirname!, relpath); + for await (const entry of Deno.readDir(root)) { + if (!entry.isFile || !entry.name.endsWith(".ts")) continue; + if (patterns.some((p) => p.test(entry.name))) continue; + yield basename(entry.name, ".ts"); + } +} + +async function listModExports(path: string): Promise { + const mod = await import(import.meta.resolve(path)); + return Array.from(Object.keys(mod)); +} + +async function listJsrExportEntries(): Promise<[string, string][]> { + const text = await Deno.readTextFile( + new URL(import.meta.resolve("../../deno.jsonc")), + ); + const json = ensure( + parse(text), + is.ObjectOf({ + exports: is.RecordOf(is.String, is.String), + }), + ); + return Object.entries(json.exports); +} diff --git a/pipe/async/pairwise.ts b/pipe/async/pairwise.ts new file mode 100644 index 0000000..2215798 --- /dev/null +++ b/pipe/async/pairwise.ts @@ -0,0 +1,19 @@ +import { pairwise } from "@core/iterutil/async/pairwise"; + +export { + /** + * An operator that pairs elements of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/pairwise/~/pairwise pairwise} for native pairwise. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { pairwise } from "@core/iterutil/async/pairwise"; + * + * const iter = pipe([1, 2, 3, 4, 5], pairwise); + * console.log(await Array.fromAsync(iter)); // [[1, 2], [2, 3], [3, 4], [4, 5]] + * ``` + */ + pairwise, +}; diff --git a/pipe/async/pairwise_test.ts b/pipe/async/pairwise_test.ts new file mode 100644 index 0000000..a42b0dd --- /dev/null +++ b/pipe/async/pairwise_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { pairwise } from "./pairwise.ts"; + +Deno.test("pairwise", async (t) => { + await t.step("usage", async () => { + const result = pipe([1, 2, 3, 4, 5], pairwise); + const expected = [[1, 2], [2, 3], [3, 4], [4, 5]]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/partition.ts b/pipe/async/partition.ts new file mode 100644 index 0000000..869cfec --- /dev/null +++ b/pipe/async/partition.ts @@ -0,0 +1,27 @@ +import { partition as base } from "@core/iterutil/async/partition"; + +/** + * Returns an operator that partitions the iterable using the provided function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/partition/~/partition partition} for native partition. + * + * @param selector The function to partition with. + * @returns An operator that partitions the iterable using the provided function. + * + * ```ts + * import { pipe } from "@core/pipe"; + * import { partition } from "@core/iterutil/pipe/async/partition"; + * + * const [even, odd] = await pipe( + * [1, 2, 3, 4, 5], + * partition((v) => v % 2 === 0), + * ); + * console.log(even); // [2, 4] + * console.log(odd); // [1, 3, 5] + * ``` + */ +export function partition( + selector: (value: T, index: number) => boolean | Promise, +): (iterable: Iterable | AsyncIterable) => Promise<[T[], T[]]> { + return (iterable) => base(iterable, selector); +} diff --git a/pipe/async/partition_test.ts b/pipe/async/partition_test.ts new file mode 100644 index 0000000..882c840 --- /dev/null +++ b/pipe/async/partition_test.ts @@ -0,0 +1,17 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { partition } from "./partition.ts"; + +Deno.test("partition", async (t) => { + await t.step("usage", async () => { + const [left, right] = await pipe( + [1, 2, 3, 4, 5], + partition((v) => v % 2 === 0), + ); + assertEquals(left, [2, 4]); + assertEquals(right, [1, 3, 5]); + assertType>(true); + assertType>(true); + }); +}); diff --git a/pipe/async/reduce.ts b/pipe/async/reduce.ts new file mode 100644 index 0000000..afee24c --- /dev/null +++ b/pipe/async/reduce.ts @@ -0,0 +1,58 @@ +import { reduce as base } from "@core/iterutil/async/reduce"; + +/** + * Returns an operator that reduces an iterable into a single value. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/reduce/~/reduce reduce} for native reduce. + * + * @param fn The function to reduce with. + * @returns An operator that reduces an iterable into a single value. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { reduce } from "@core/iterutil/pipe/async/reduce"; + * + * const result = await pipe( + * [1, 2, 3, 4, 5], + * reduce((acc, v) => acc + v), + * ); + * console.log(result); // 15 + * ``` + */ +export function reduce( + fn: (acc: T, value: T, index: number) => T | Promise, +): (iterable: Iterable | AsyncIterable) => Promise; + +/** + * Returns an operator that reduces an iterable into a single value. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/reduce/~/reduce reduce} for native reduce. + * + * @param fn The function to reduce with. + * @param initial The initial value to start reducing with. + * @returns An operator that reduces an iterable into a single value. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { reduce } from "@core/iterutil/pipe/async/reduce"; + * + * const result = await pipe( + * [1, 2, 3, 4, 5], + * reduce((acc, v) => acc + v), + * ); + * console.log(result); // 12345 + * ``` + */ +export function reduce( + fn: (acc: U, value: T, index: number) => U | Promise, + initial: U, +): (iterable: Iterable | AsyncIterable) => Promise; + +export function reduce( + fn: (acc: U, value: T, index: number) => U | Promise, + initial?: U, +): (iterable: Iterable | AsyncIterable) => Promise { + return (iterable) => base(iterable, fn, initial as U); +} diff --git a/pipe/async/reduce_test.ts b/pipe/async/reduce_test.ts new file mode 100644 index 0000000..e9b3654 --- /dev/null +++ b/pipe/async/reduce_test.ts @@ -0,0 +1,24 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { reduce } from "./reduce.ts"; + +Deno.test("reduce", async (t) => { + await t.step("usage 1", async () => { + const result = await pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v), + ); + assertEquals(result, 15); + assertType>(true); + }); + + await t.step("usage 2", async () => { + const result = await pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v, ""), + ); + assertEquals(result, "12345"); + assertType>(true); + }); +}); diff --git a/pipe/async/some.ts b/pipe/async/some.ts new file mode 100644 index 0000000..6087160 --- /dev/null +++ b/pipe/async/some.ts @@ -0,0 +1,27 @@ +import { some as base } from "@core/iterutil/async/some"; + +/** + * Returns an operator that tests whether at least one element in the iterable satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/some/~/some some} for native some. + * + * @param fn The function to check with. + * @returns An operator that tests whether at least one element in the iterable satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { some } from "@core/iterutil/pipe/async/some"; + * + * console.log(await pipe( + * [1 ,2, 3], + * some((v) => v % 2 === 0), + * )); // true + * console.log(await pipe([1, 3, 5], some((v) => v % 2 === 0))); // false + * ``` + */ +export function some( + fn: (value: T) => boolean | Promise, +): (iterable: Iterable | AsyncIterable) => Promise { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/some_test.ts b/pipe/async/some_test.ts new file mode 100644 index 0000000..40b51f1 --- /dev/null +++ b/pipe/async/some_test.ts @@ -0,0 +1,20 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { some } from "./some.ts"; + +Deno.test("some", async (t) => { + await t.step("usage 1", async () => { + const result = await pipe([1, 2, 3], some((v) => v % 2 === 0)); + const expected = true; + assertEquals(result, expected); + assertType>(true); + }); + + await t.step("usage 2", async () => { + const result = await pipe([1, 3, 5], some((v) => v % 2 === 0)); + const expected = false; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/async/take.ts b/pipe/async/take.ts new file mode 100644 index 0000000..cd967f9 --- /dev/null +++ b/pipe/async/take.ts @@ -0,0 +1,24 @@ +import { take as base } from "@core/iterutil/async/take"; +/** + * Returns an operator that takes the first `limit` items from the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/take/~/take take} for native take. + * + * @param limit The number of items to take. It must be 0 or positive safe integer. + * @returns An operator that takes the first `limit` items from the iterable. + * @throws {RangeError} if `limit` is less than 0 or non safe integer. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { take } from "@core/iterutil/pipe/async/take"; + * + * const iter = pipe([1, 2, 3, 4, 5], take(2)); + * console.log(await Array.fromAsync(iter)); // [1, 2] + * ``` + */ +export function take( + limit: number, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, limit); +} diff --git a/pipe/async/take_test.ts b/pipe/async/take_test.ts new file mode 100644 index 0000000..278d808 --- /dev/null +++ b/pipe/async/take_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { take } from "./take.ts"; + +Deno.test("take", async (t) => { + await t.step("usage 1", async () => { + const result = pipe([1, 2, 3, 4, 5], take(2)); + const expected = [1, 2]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/take_while.ts b/pipe/async/take_while.ts new file mode 100644 index 0000000..1caa658 --- /dev/null +++ b/pipe/async/take_while.ts @@ -0,0 +1,27 @@ +import { takeWhile as base } from "@core/iterutil/async/take-while"; + +/** + * Returns an operator that takes elements from the iterable while the predicate returns `true`. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/take-while/~/takeWhile takeWhile} for native takeWhile. + * + * @param fn The predicate to take elements with. + * @returns An operator that takes elements from the iterable while the predicate returns `true`. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { takeWhile } from "@core/iterutil/pipe/async/take-while"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * takeWhile((v) => v < 4), + * ); + * console.log(await Array.fromAsync(iter)); // [1, 2, 3] + * ``` + */ +export function takeWhile( + fn: (value: T, index: number) => boolean | Promise, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/async/take_while_test.ts b/pipe/async/take_while_test.ts new file mode 100644 index 0000000..8f1b453 --- /dev/null +++ b/pipe/async/take_while_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { takeWhile } from "./take_while.ts"; + +Deno.test("takeWhile", async (t) => { + await t.step("usage", async () => { + const result = pipe([1, 2, 3, 4, 5], takeWhile((v) => v < 4)); + const expected = [1, 2, 3]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/uniq.ts b/pipe/async/uniq.ts new file mode 100644 index 0000000..09e61a4 --- /dev/null +++ b/pipe/async/uniq.ts @@ -0,0 +1,30 @@ +import { uniq as base } from "@core/iterutil/async/uniq"; + +/** + * Returns an operator that yields the unique elements of the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/uniq/~/uniq uniq} for native uniq. + * + * @param identify An optional function to transform the elements before checking for uniqueness. + * @returns An operator that yields the unique elements of the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { uniq } from "@core/iterutil/pipe/async/uniq"; + * + * const iter1 = pipe([1, 2, 2, 3, 3, 3], uniq()); + * console.log(await Array.fromAsync(iter1)); // [1, 2, 3] + * + * const iter2 = pipe( + * [1, 2, 3, 4, 5, 6, 7, 8, 9], + * uniq((v) => v % 4), + * ); + * console.log(await Array.fromAsync(iter2)); // [1, 2, 3, 4] + * ``` + */ +export function uniq( + identify: (v: T, index: number) => unknown | Promise = (v) => v, +): (iterable: Iterable | AsyncIterable) => AsyncIterable { + return (iterable) => base(iterable, identify); +} diff --git a/pipe/async/uniq_test.ts b/pipe/async/uniq_test.ts new file mode 100644 index 0000000..9cfac74 --- /dev/null +++ b/pipe/async/uniq_test.ts @@ -0,0 +1,23 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { uniq } from "./uniq.ts"; + +Deno.test("uniq", async (t) => { + await t.step("usage 1", async () => { + const result = pipe([1, 2, 2, 3, 3, 3], uniq()); + const expected = [1, 2, 3]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); + + await t.step("with identify", async () => { + const result = pipe( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + uniq((v) => v % 4), + ); + const expected = [1, 2, 3, 4]; + assertEquals(await Array.fromAsync(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/async/zip.ts b/pipe/async/zip.ts new file mode 100644 index 0000000..1f8bdc9 --- /dev/null +++ b/pipe/async/zip.ts @@ -0,0 +1,35 @@ +import { type Zip, zip as base } from "@core/iterutil/async/zip"; + +/** + * Returns an operator that zips the provided iterables with the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/async/zip/~/zip zip} for native zip. + * + * @param iterables The iterables to zip. + * @returns An operator that zips the provided iterables with the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { zip } from "@core/iterutil/pipe/async/zip"; + * + * const iter = pipe( + * [1, 2, 3], + * zip(["a", "b", "c"], [true, true, false]), + * ); + * console.log(await Array.fromAsync(iter)); // [[1, "a", true], [2, "b", true], [3, "c", false]] + * ``` + */ +export function zip< + U extends readonly [ + Iterable | AsyncIterable, + ...(Iterable | AsyncIterable)[], + ], +>( + ...iterables: U +): ( + iterable: Iterable | AsyncIterable, +) => AsyncIterable<[T, ...Zip]> { + return (iterable: Iterable | AsyncIterable) => + base(iterable, ...iterables) as AsyncIterable<[T, ...Zip]>; +} diff --git a/pipe/async/zip_test.ts b/pipe/async/zip_test.ts new file mode 100644 index 0000000..d21bc1d --- /dev/null +++ b/pipe/async/zip_test.ts @@ -0,0 +1,17 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { zip } from "./zip.ts"; + +Deno.test("zip", async (t) => { + await t.step("usage", async () => { + const result = pipe([1, 2, 3], zip(["a", "b", "c"], [true, false, true])); + const expected = [[1, "a", true], [2, "b", false], [3, "c", true]]; + assertEquals(await Array.fromAsync(result), expected); + assertType< + IsExact> + >( + true, + ); + }); +}); diff --git a/pipe/chain.ts b/pipe/chain.ts new file mode 100644 index 0000000..5ef7a88 --- /dev/null +++ b/pipe/chain.ts @@ -0,0 +1,33 @@ +import { type Chain, chain as base } from "@core/iterutil/chain"; + +/** + * Returns an operator that chains multiple iterables to the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/chain/~/chain chain} for native chain. + * + * @param iterables The iterables to chain to the iterable. + * @returns An operator that chains multiple iterables to the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { chain } from "@core/iterutil/pipe/chain"; + * + * const iter = pipe( + * [1, 2, 3], + * chain(["a", "b"], [true]), + * ); + * console.log(Array.from(iter)); // [1, 2, 3, "a", "b", true] + * ``` + */ +export function chain< + U extends readonly [ + Iterable, + ...Iterable[], + ], +>( + ...iterables: U +): (iterable: Iterable) => Iterable> { + return (iterable: Iterable) => + base(iterable, ...iterables) as Iterable>; +} diff --git a/pipe/chain_test.ts b/pipe/chain_test.ts new file mode 100644 index 0000000..8c7860d --- /dev/null +++ b/pipe/chain_test.ts @@ -0,0 +1,18 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { chain } from "./chain.ts"; + +Deno.test("chain", async (t) => { + await t.step("usage", () => { + const result = pipe( + [1, 2, 3], + chain(["a", "b"], [true]), + ); + const expected = [1, 2, 3, "a", "b", true]; + assertEquals(Array.from(result), expected); + assertType>>( + true, + ); + }); +}); diff --git a/pipe/chunked.ts b/pipe/chunked.ts new file mode 100644 index 0000000..8269ae4 --- /dev/null +++ b/pipe/chunked.ts @@ -0,0 +1,27 @@ +import { chunked as base } from "@core/iterutil/chunked"; + +/** + * Returns an operator that chunks the iterable into arrays of `size`. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/chunked/~/chunked chunked} for native chunked. + * + * @param size The size of each chunk. + * @return An operator that chunks the iterable into arrays of `size`. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { chunked } from "@core/iterutil/pipe/chunked"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * chunked(2), + * ); + * console.log(Array.from(iter)); // [[1, 2], [3, 4], [5]] + * ``` + */ +export function chunked( + size: number, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, size); +} diff --git a/pipe/chunked_test.ts b/pipe/chunked_test.ts new file mode 100644 index 0000000..ca6ff01 --- /dev/null +++ b/pipe/chunked_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { chunked } from "./chunked.ts"; + +Deno.test("chunked", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3, 4, 5, 6], chunked(2)); + const expected = [[1, 2], [3, 4], [5, 6]]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/compact.ts b/pipe/compact.ts new file mode 100644 index 0000000..1c1ac6b --- /dev/null +++ b/pipe/compact.ts @@ -0,0 +1,22 @@ +import { compact } from "@core/iterutil/compact"; + +export { + /** + * An operator to remove all nullish (`null` or `undefined`) values from an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/compact/~/compact compact} for native compact. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { compact } from "@core/iterutil/pipe/compact"; + * + * const iter = pipe( + * [1, undefined, 2, null, 3], + * compact, + * ); + * console.log(Array.from(iter)); // [1, 2, 3] + * ``` + */ + compact, +}; diff --git a/pipe/compact_test.ts b/pipe/compact_test.ts new file mode 100644 index 0000000..4c31d61 --- /dev/null +++ b/pipe/compact_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { compact } from "./compact.ts"; + +Deno.test("compact", async (t) => { + await t.step("usage", () => { + const result = pipe([1, undefined, 2, null, 3], compact); + const expected = [1, 2, 3]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/compress.ts b/pipe/compress.ts new file mode 100644 index 0000000..b963ed0 --- /dev/null +++ b/pipe/compress.ts @@ -0,0 +1,27 @@ +import { compress as base } from "@core/iterutil/compress"; + +/** + * Returns an operator that compresses an iterable by selecting elements using a selector iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/compress/~/compress compress} for native compress. + * + * @param selectors The selectors to use. + * @returns An operator that compresses an iterable by selecting elements using a selector iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { compress } from "@core/iterutil/pipe/compress"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * compress([true, false, true, false, true]), + * ); + * console.log(Array.from(iter)); // [1, 3, 5] + * ``` + */ +export function compress( + selectors: Iterable, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, selectors); +} diff --git a/pipe/compress_test.ts b/pipe/compress_test.ts new file mode 100644 index 0000000..a082bc3 --- /dev/null +++ b/pipe/compress_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { compress } from "./compress.ts"; + +Deno.test("compress", async (t) => { + await t.step("usage", () => { + const result = pipe( + [1, 2, 3, 4, 5], + compress([true, false, true, false, true]), + ); + const expected = [1, 3, 5]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/cycle.ts b/pipe/cycle.ts new file mode 100644 index 0000000..c6de48f --- /dev/null +++ b/pipe/cycle.ts @@ -0,0 +1,24 @@ +import { cycle } from "@core/iterutil/cycle"; + +export { + /** + * An operator to return a function that cycles the elements of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/cycle/~/cycle cycle} for native cycle. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { cycle } from "@core/iterutil/pipe/cycle"; + * import { take } from "@core/iterutil/pipe/take"; + * + * const iter = pipe( + * [1, 2, 3], + * cycle, + * take(5), + * ); + * console.log(Array.from(iter)); // [1, 2, 3, 1, 2] + * ``` + */ + cycle, +}; diff --git a/pipe/cycle_test.ts b/pipe/cycle_test.ts new file mode 100644 index 0000000..efde2ea --- /dev/null +++ b/pipe/cycle_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { take } from "./take.ts"; +import { cycle } from "./cycle.ts"; + +Deno.test("cycle", async (t) => { + await t.step("usage", () => { + const result = pipe([0, 1, 2], cycle, take(5)); + const expected = [0, 1, 2, 0, 1]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/drop.ts b/pipe/drop.ts new file mode 100644 index 0000000..0d2316b --- /dev/null +++ b/pipe/drop.ts @@ -0,0 +1,26 @@ +import { drop as base } from "@core/iterutil/drop"; + +/** + * Returns an operator that drops the first `limit` items from the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/drop/~/drop drop} for native drop. + * + * @param limit The number of items to drop. It must be 0 or positive safe integer. + * @returns An operator that drops the first `limit` items from the iterable. + * @throws {RangeError} if `limit` is less than 0 or non safe integer. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { drop } from "@core/iterutil/pipe/drop"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * drop(2), + * ); + * console.log(Array.from(iter)); // [3, 4, 5] + * ``` + */ +export function drop(limit: number): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, limit); +} diff --git a/pipe/drop_test.ts b/pipe/drop_test.ts new file mode 100644 index 0000000..e137b0b --- /dev/null +++ b/pipe/drop_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { drop } from "./drop.ts"; + +Deno.test("drop", async (t) => { + await t.step("usage", () => { + const result = pipe([0, 1, 2, 3, 4], drop(2)); + const expected = [2, 3, 4]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/drop_while.ts b/pipe/drop_while.ts new file mode 100644 index 0000000..2412989 --- /dev/null +++ b/pipe/drop_while.ts @@ -0,0 +1,27 @@ +import { dropWhile as base } from "@core/iterutil/drop-while"; + +/** + * Returns an operator that drops elements from the iterable while the predicate returns true. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/drop-while/~/dropWhile dropWhile} for native dropWhile. + * + * @param fn The predicate function. + * @returns An operator that drops elements from the iterable while the predicate returns true. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { dropWhile } from "@core/iterutil/pipe/drop-while"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * dropWhile((v) => v < 3), + * ); + * console.log(Array.from(iter)); // [3, 4, 5] + * ``` + */ +export function dropWhile( + fn: (value: T) => boolean, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/drop_while_test.ts b/pipe/drop_while_test.ts new file mode 100644 index 0000000..31271e5 --- /dev/null +++ b/pipe/drop_while_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { dropWhile } from "./drop_while.ts"; + +Deno.test("dropWhile", async (t) => { + await t.step("usage", () => { + const result = pipe( + [0, 1, 2, 3, 4], + dropWhile((v) => v < 2), + ); + const expected = [2, 3, 4]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/enumerate.ts b/pipe/enumerate.ts new file mode 100644 index 0000000..34eb37d --- /dev/null +++ b/pipe/enumerate.ts @@ -0,0 +1,41 @@ +import { enumerate as base } from "@core/iterutil/enumerate"; + +/** + * Returns an operator that enumerates the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/enumerate/~/enumerate enumerate} for native enumerate. + * + * @param start The starting index. + * @param step The step between indices. + * @returns An operator that enumerates the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { enumerate } from "@core/iterutil/pipe/enumerate"; + * + * const iter1 = pipe( + * ["a", "b", "c"], + * enumerate(), + * ); + * console.log(Array.from(iter1)); // [[0, "a"], [1, "b"], [2, "c"]] + * + * const iter2 = pipe( + * ["a", "b", "c"], + * enumerate(1), + * ); + * console.log(Array.from(iter2)); // [[1, "a"], [2, "b"], [3, "c"]] + * + * const iter3 = pipe( + * ["a", "b", "c"], + * enumerate(1, 2), + * ); + * console.log(Array.from(iter3)); // [[1, "a"], [3, "b"], [5, "c"]] + * ``` + */ +export function enumerate( + start: number = 0, + step: number = 1, +): (iterable: Iterable) => Iterable<[number, T]> { + return (iterable) => base(iterable, start, step); +} diff --git a/pipe/enumerate_test.ts b/pipe/enumerate_test.ts new file mode 100644 index 0000000..fab47bf --- /dev/null +++ b/pipe/enumerate_test.ts @@ -0,0 +1,27 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { enumerate } from "./enumerate.ts"; + +Deno.test("enumerate", async (t) => { + await t.step("usage 1", () => { + const result = pipe(["a", "b", "c"], enumerate()); + const expected = [[0, "a"], [1, "b"], [2, "c"]]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); + + await t.step("usage 2", () => { + const result = pipe(["a", "b", "c"], enumerate(1)); + const expected = [[1, "a"], [2, "b"], [3, "c"]]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); + + await t.step("usage 3", () => { + const result = pipe(["a", "b", "c"], enumerate(1, 2)); + const expected = [[1, "a"], [3, "b"], [5, "c"]]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/every.ts b/pipe/every.ts new file mode 100644 index 0000000..519bcea --- /dev/null +++ b/pipe/every.ts @@ -0,0 +1,27 @@ +import { every as base } from "@core/iterutil/every"; + +/** + * Returns an operator that tests whether every element in the iterable satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/every/~/every every} for native every. + * + * @param fn The testing function. + * @returns A function that tests whether every element in the iterable satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { every } from "@core/iterutil/pipe/every"; + * + * const value = pipe( + * [1, 2, 3], + * every((v) => v > 0), + * ); + * console.log(value); // true + * ``` + */ +export function every( + fn: (value: T) => boolean, +): (iterable: Iterable) => boolean { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/every_test.ts b/pipe/every_test.ts new file mode 100644 index 0000000..0f47f24 --- /dev/null +++ b/pipe/every_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { every } from "./every.ts"; + +Deno.test("every", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3], every((v) => v > 0)); + const expected = true; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/filter.ts b/pipe/filter.ts new file mode 100644 index 0000000..4b54f14 --- /dev/null +++ b/pipe/filter.ts @@ -0,0 +1,27 @@ +import { filter as base } from "@core/iterutil/filter"; + +/** + * Returns an operator that filters an iterable based on a function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/filter/~/filter filter} for native filter. + * + * @params fn The function to filter the iterable. + * @returns An operator that filters an iterable based on a function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { filter } from "@core/iterutil/pipe/filter"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * filter((v) => v % 2 === 0), + * ); + * console.log(Array.from(iter)); // [2, 4] + * ``` + */ +export function filter( + fn: (value: T, index: number) => boolean, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/filter_test.ts b/pipe/filter_test.ts new file mode 100644 index 0000000..8121546 --- /dev/null +++ b/pipe/filter_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { filter } from "./filter.ts"; + +Deno.test("filter", () => { + const result = pipe( + [1, 2, 3, 4, 5], + filter((v) => v % 2 === 0), + ); + const expected = [2, 4]; + assertEquals(Array.from(result), expected); + assertType>>(true); +}); diff --git a/pipe/find.ts b/pipe/find.ts new file mode 100644 index 0000000..7b901b9 --- /dev/null +++ b/pipe/find.ts @@ -0,0 +1,27 @@ +import { find as base } from "@core/iterutil/find"; + +/** + * Returns an operator that finds the first element that satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/find/~/find find} for native find. + * + * @param fn The testing function. + * @returns An operator that finds the first element that satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { find } from "@core/iterutil/pipe/find"; + * + * const value = pipe( + * [1, 2, 3, 4, 5], + * find((v) => v % 2 === 0), + * ); + * console.log(value); // 2 + * ``` + */ +export function find( + fn: (value: T, index: number) => boolean, +): (iterable: Iterable) => T | undefined { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/find_test.ts b/pipe/find_test.ts new file mode 100644 index 0000000..147488e --- /dev/null +++ b/pipe/find_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { find } from "./find.ts"; + +Deno.test("find", async (t) => { + await t.step("usage", () => { + const result = pipe( + [1, 2, 3, 4, 5], + find((v) => v % 2 === 0), + ); + const expected = 2; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/first.ts b/pipe/first.ts new file mode 100644 index 0000000..76cf671 --- /dev/null +++ b/pipe/first.ts @@ -0,0 +1,22 @@ +import { first } from "@core/iterutil/first"; + +export { + /** + * An operator that gets the first element of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/first/~/first first} for native first. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { first } from "@core/iterutil/pipe/first"; + * + * const value = pipe( + * [1, 2, 3], + * first, + * ); + * console.log(value); // 1 + * ``` + */ + first, +}; diff --git a/pipe/first_test.ts b/pipe/first_test.ts new file mode 100644 index 0000000..46499e5 --- /dev/null +++ b/pipe/first_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { first } from "./first.ts"; + +Deno.test("first", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3], first); + const expected = 1; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/flat_map.ts b/pipe/flat_map.ts new file mode 100644 index 0000000..24e840d --- /dev/null +++ b/pipe/flat_map.ts @@ -0,0 +1,27 @@ +import { flatMap as base } from "@core/iterutil/flat-map"; + +/** + * Returns an operator that flat maps the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/flat-map/~/flatMap flatMap} for native flatMap. + * + * @param fn The flat mapping function. + * @returns An operator that flat maps the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { flatMap } from "@core/iterutil/pipe/flat-map"; + * + * const iter = pipe( + * [1, 2, 3], + * flatMap((v) => [v, v]), + * ); + * console.log(Array.from(iter)); // [1, 1, 2, 2, 3, 3] + * ``` + */ +export function flatMap( + fn: (value: T, index: number) => Iterable, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/flat_map_test.ts b/pipe/flat_map_test.ts new file mode 100644 index 0000000..4d515a5 --- /dev/null +++ b/pipe/flat_map_test.ts @@ -0,0 +1,16 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { flatMap } from "./flat_map.ts"; + +Deno.test("flatMap", async (t) => { + await t.step("usage", () => { + const result = pipe( + [1, 2, 3], + flatMap((v) => [v, v]), + ); + const expected = [1, 1, 2, 2, 3, 3]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/flatten.ts b/pipe/flatten.ts new file mode 100644 index 0000000..8291fd1 --- /dev/null +++ b/pipe/flatten.ts @@ -0,0 +1,22 @@ +import { flatten } from "@core/iterutil/flatten"; + +export { + /** + * An operator that flattens an iterable of iterables. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/flatten/~/flatten flatten} for native flatten. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { flatten } from "@core/iterutil/pipe/flatten"; + * + * const iter = pipe( + * [[1, 2], [3, 4], [5]], + * flatten, + * ); + * console.log(Array.from(iter)); // [1, 2, 3, 4, 5] + * ``` + */ + flatten, +}; diff --git a/pipe/flatten_test.ts b/pipe/flatten_test.ts new file mode 100644 index 0000000..ed6c324 --- /dev/null +++ b/pipe/flatten_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { flatten } from "./flatten.ts"; + +Deno.test("flatten", async (t) => { + await t.step("usage", () => { + const result = pipe([[1, 2], [3, 4], [5]], flatten); + const expected = [1, 2, 3, 4, 5]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/for_each.ts b/pipe/for_each.ts new file mode 100644 index 0000000..6d282ea --- /dev/null +++ b/pipe/for_each.ts @@ -0,0 +1,29 @@ +import { forEach as base } from "@core/iterutil/for-each"; + +/** + * Returns an operator that calls the given function for each value in the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/for-each/~/forEach forEach} for native forEach. + * + * @param fn The function to call for each value. + * @returns An operator that calls the given function for each value in the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { forEach } from "@core/iterutil/pipe/for-each"; + * + * pipe( + * [1, 2, 3], + * forEach((v) => console.log(v)), + * ); + * // 1 + * // 2 + * // 3 + * ``` + */ +export function forEach( + fn: (value: T, index: number) => void, +): (iterable: Iterable) => void { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/for_each_test.ts b/pipe/for_each_test.ts new file mode 100644 index 0000000..af16710 --- /dev/null +++ b/pipe/for_each_test.ts @@ -0,0 +1,12 @@ +import { assertEquals } from "@std/assert"; +import { pipe } from "@core/pipe"; +import { forEach } from "./for_each.ts"; + +Deno.test("forEach", () => { + const values: number[] = []; + pipe( + [1, 2, 3, 4, 5], + forEach((v) => values.push(v)), + ); + assertEquals(values, [1, 2, 3, 4, 5]); +}); diff --git a/pipe/last.ts b/pipe/last.ts new file mode 100644 index 0000000..23fae91 --- /dev/null +++ b/pipe/last.ts @@ -0,0 +1,22 @@ +import { last } from "@core/iterutil/last"; + +export { + /** + * An operator that gets the last element of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/last/~/last last} for native last. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { last } from "@core/iterutil/pipe/last"; + * + * const value = pipe( + * [1, 2, 3], + * last + * ); + * console.log(value); // 3 + * ``` + */ + last, +}; diff --git a/pipe/last_test.ts b/pipe/last_test.ts new file mode 100644 index 0000000..6479013 --- /dev/null +++ b/pipe/last_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { last } from "./last.ts"; + +Deno.test("last", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3], last); + const expected = 3; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/map.ts b/pipe/map.ts new file mode 100644 index 0000000..dbd51c0 --- /dev/null +++ b/pipe/map.ts @@ -0,0 +1,27 @@ +import { map as base } from "@core/iterutil/map"; + +/** + * Returns an operator that maps the iterable using the provided function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/map/~/map map} for native map. + * + * @param fn The mapping function. + * @returns An operator that maps the iterable using the provided function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { map } from "@core/iterutil/pipe/map"; + * + * const iter = pipe( + * [1, 2, 3], + * map((v) => v * 2), + * ); + * console.log(Array.from(iter)); // [2, 4, 6] + * ``` + */ +export function map( + fn: (value: T, index: number) => U, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/map_test.ts b/pipe/map_test.ts new file mode 100644 index 0000000..6f53640 --- /dev/null +++ b/pipe/map_test.ts @@ -0,0 +1,14 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { map } from "./map.ts"; + +Deno.test("map", () => { + const result = pipe( + [1, 2, 3, 4, 5], + map((v) => v * 2), + ); + const expected = [2, 4, 6, 8, 10]; + assertEquals(Array.from(result), expected); + assertType>>(true); +}); diff --git a/pipe/mod.ts b/pipe/mod.ts new file mode 100644 index 0000000..ea79330 --- /dev/null +++ b/pipe/mod.ts @@ -0,0 +1,25 @@ +export * from "./chain.ts"; +export * from "./chunked.ts"; +export * from "./compact.ts"; +export * from "./compress.ts"; +export * from "./cycle.ts"; +export * from "./drop.ts"; +export * from "./drop_while.ts"; +export * from "./enumerate.ts"; +export * from "./every.ts"; +export * from "./filter.ts"; +export * from "./find.ts"; +export * from "./first.ts"; +export * from "./flat_map.ts"; +export * from "./flatten.ts"; +export * from "./for_each.ts"; +export * from "./last.ts"; +export * from "./map.ts"; +export * from "./pairwise.ts"; +export * from "./partition.ts"; +export * from "./reduce.ts"; +export * from "./some.ts"; +export * from "./take.ts"; +export * from "./take_while.ts"; +export * from "./uniq.ts"; +export * from "./zip.ts"; diff --git a/pipe/mod_test.ts b/pipe/mod_test.ts new file mode 100644 index 0000000..d7a6ef1 --- /dev/null +++ b/pipe/mod_test.ts @@ -0,0 +1,59 @@ +import { assertArrayIncludes } from "@std/assert"; +import { basename, globToRegExp, join } from "@std/path"; +import { ensure, is } from "@core/unknownutil"; +import { parse } from "@std/jsonc"; + +const excludes = [ + "mod.ts", + "*_test.ts", + "*_bench.ts", +]; + +Deno.test("mod.ts must exports all exports in public modules", async () => { + const modExports = await listModExports("./mod.ts"); + const pubExports = []; + for await (const name of iterPublicModules(".")) { + pubExports.push(...await listModExports(`./${name}.ts`)); + } + assertArrayIncludes(modExports, pubExports); +}); + +Deno.test("JSR exports must have all exports in mod.ts", async () => { + const jsrExportEntries = await listJsrExportEntries(); + const modExportEntries: [string, string][] = []; + for await (const name of iterPublicModules(".")) { + modExportEntries.push([ + `./pipe/${name.replaceAll("_", "-")}`, + `./pipe/${name}.ts`, + ]); + } + assertArrayIncludes(jsrExportEntries, modExportEntries); +}); + +async function* iterPublicModules(relpath: string): AsyncIterable { + const patterns = excludes.map((p) => globToRegExp(p)); + const root = join(import.meta.dirname!, relpath); + for await (const entry of Deno.readDir(root)) { + if (!entry.isFile || !entry.name.endsWith(".ts")) continue; + if (patterns.some((p) => p.test(entry.name))) continue; + yield basename(entry.name, ".ts"); + } +} + +async function listModExports(path: string): Promise { + const mod = await import(import.meta.resolve(path)); + return Array.from(Object.keys(mod)); +} + +async function listJsrExportEntries(): Promise<[string, string][]> { + const text = await Deno.readTextFile( + new URL(import.meta.resolve("../deno.jsonc")), + ); + const json = ensure( + parse(text), + is.ObjectOf({ + exports: is.RecordOf(is.String, is.String), + }), + ); + return Object.entries(json.exports); +} diff --git a/pipe/pairwise.ts b/pipe/pairwise.ts new file mode 100644 index 0000000..6ef0503 --- /dev/null +++ b/pipe/pairwise.ts @@ -0,0 +1,19 @@ +import { pairwise } from "@core/iterutil/pairwise"; + +export { + /** + * An operator that pairs elements of an iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/pairwise/~/pairwise pairwise} for native pairwise. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { pairwise } from "@core/iterutil/pairwise"; + * + * const iter = pipe([1, 2, 3, 4, 5], pairwise); + * console.log(Array.from(iter)); // [[1, 2], [2, 3], [3, 4], [4, 5]] + * ``` + */ + pairwise, +}; diff --git a/pipe/pairwise_test.ts b/pipe/pairwise_test.ts new file mode 100644 index 0000000..b4e2f7a --- /dev/null +++ b/pipe/pairwise_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { pairwise } from "./pairwise.ts"; + +Deno.test("pairwise", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3, 4, 5], pairwise); + const expected = [[1, 2], [2, 3], [3, 4], [4, 5]]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/partition.ts b/pipe/partition.ts new file mode 100644 index 0000000..ad646ec --- /dev/null +++ b/pipe/partition.ts @@ -0,0 +1,27 @@ +import { partition as base } from "@core/iterutil/partition"; + +/** + * Returns an operator that partitions the iterable using the provided function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/partition/~/partition partition} for native partition. + * + * @param selector The function to partition with. + * @returns An operator that partitions the iterable using the provided function. + * + * ```ts + * import { pipe } from "@core/pipe"; + * import { partition } from "@core/iterutil/pipe/partition"; + * + * const [even, odd] = pipe( + * [1, 2, 3, 4, 5], + * partition((v) => v % 2 === 0), + * ); + * console.log(even); // [2, 4] + * console.log(odd); // [1, 3, 5] + * ``` + */ +export function partition( + selector: (value: T, index: number) => boolean, +): (iterable: Iterable) => [T[], T[]] { + return (iterable) => base(iterable, selector); +} diff --git a/pipe/partition_test.ts b/pipe/partition_test.ts new file mode 100644 index 0000000..5a9c693 --- /dev/null +++ b/pipe/partition_test.ts @@ -0,0 +1,17 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { partition } from "./partition.ts"; + +Deno.test("partition", async (t) => { + await t.step("usage", () => { + const [left, right] = pipe( + [1, 2, 3, 4, 5], + partition((v) => v % 2 === 0), + ); + assertEquals(left, [2, 4]); + assertEquals(right, [1, 3, 5]); + assertType>(true); + assertType>(true); + }); +}); diff --git a/pipe/reduce.ts b/pipe/reduce.ts new file mode 100644 index 0000000..28e8883 --- /dev/null +++ b/pipe/reduce.ts @@ -0,0 +1,58 @@ +import { reduce as base } from "@core/iterutil/reduce"; + +/** + * Returns an operator that reduces an iterable into a single value. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/reduce/~/reduce reduce} for native reduce. + * + * @param fn The function to reduce with. + * @returns An operator that reduces an iterable into a single value. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { reduce } from "@core/iterutil/pipe/reduce"; + * + * const result = pipe( + * [1, 2, 3, 4, 5], + * reduce((acc, v) => acc + v), + * ); + * console.log(result); // 15 + * ``` + */ +export function reduce( + fn: (acc: T, value: T, index: number) => T, +): (iterable: Iterable) => T | undefined; + +/** + * Returns an operator that reduces an iterable into a single value. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/reduce/~/reduce reduce} for native reduce. + * + * @param fn The function to reduce with. + * @param initial The initial value to start reducing with. + * @returns An operator that reduces an iterable into a single value. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { reduce } from "@core/iterutil/pipe/reduce"; + * + * const result = pipe( + * [1, 2, 3, 4, 5], + * reduce((acc, v) => acc + v), + * ); + * console.log(result); // 12345 + * ``` + */ +export function reduce( + fn: (acc: U, value: T, index: number) => U, + initial: U, +): (iterable: Iterable) => U; + +export function reduce( + fn: (acc: U, value: T, index: number) => U, + initial?: U, +): (iterable: Iterable) => U | undefined { + return (iterable) => base(iterable, fn, initial as U); +} diff --git a/pipe/reduce_test.ts b/pipe/reduce_test.ts new file mode 100644 index 0000000..77055f7 --- /dev/null +++ b/pipe/reduce_test.ts @@ -0,0 +1,24 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { reduce } from "./reduce.ts"; + +Deno.test("reduce", async (t) => { + await t.step("usage 1", () => { + const result = pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v), + ); + assertEquals(result, 15); + assertType>(true); + }); + + await t.step("usage 2", () => { + const result = pipe( + [1, 2, 3, 4, 5], + reduce((acc, v) => acc + v, ""), + ); + assertEquals(result, "12345"); + assertType>(true); + }); +}); diff --git a/pipe/some.ts b/pipe/some.ts new file mode 100644 index 0000000..2ad91ce --- /dev/null +++ b/pipe/some.ts @@ -0,0 +1,27 @@ +import { some as base } from "@core/iterutil/some"; + +/** + * Returns an operator that tests whether at least one element in the iterable satisfies the provided testing function. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/some/~/some some} for native some. + * + * @param fn The function to check with. + * @returns An operator that tests whether at least one element in the iterable satisfies the provided testing function. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { some } from "@core/iterutil/pipe/some"; + * + * console.log(pipe( + * [1 ,2, 3], + * some((v) => v % 2 === 0), + * )); // true + * console.log(pipe([1, 3, 5], some((v) => v % 2 === 0))); // false + * ``` + */ +export function some( + fn: (value: T) => boolean, +): (iterable: Iterable) => boolean { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/some_test.ts b/pipe/some_test.ts new file mode 100644 index 0000000..8a535aa --- /dev/null +++ b/pipe/some_test.ts @@ -0,0 +1,20 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { some } from "./some.ts"; + +Deno.test("some", async (t) => { + await t.step("usage 1", () => { + const result = pipe([1, 2, 3], some((v) => v % 2 === 0)); + const expected = true; + assertEquals(result, expected); + assertType>(true); + }); + + await t.step("usage 2", () => { + const result = pipe([1, 3, 5], some((v) => v % 2 === 0)); + const expected = false; + assertEquals(result, expected); + assertType>(true); + }); +}); diff --git a/pipe/take.ts b/pipe/take.ts new file mode 100644 index 0000000..c56c864 --- /dev/null +++ b/pipe/take.ts @@ -0,0 +1,22 @@ +import { take as base } from "@core/iterutil/take"; +/** + * Returns an operator that takes the first `limit` items from the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/take/~/take take} for native take. + * + * @param limit The number of items to take. It must be 0 or positive safe integer. + * @returns An operator that takes the first `limit` items from the iterable. + * @throws {RangeError} if `limit` is less than 0 or non safe integer. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { take } from "@core/iterutil/pipe/take"; + * + * const iter = pipe([1, 2, 3, 4, 5], take(2)); + * console.log(Array.from(iter)); // [1, 2] + * ``` + */ +export function take(limit: number): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, limit); +} diff --git a/pipe/take_test.ts b/pipe/take_test.ts new file mode 100644 index 0000000..1cc1d09 --- /dev/null +++ b/pipe/take_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { take } from "./take.ts"; + +Deno.test("take", async (t) => { + await t.step("usage 1", () => { + const result = pipe([1, 2, 3, 4, 5], take(2)); + const expected = [1, 2]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/take_while.ts b/pipe/take_while.ts new file mode 100644 index 0000000..8e405da --- /dev/null +++ b/pipe/take_while.ts @@ -0,0 +1,27 @@ +import { takeWhile as base } from "@core/iterutil/take-while"; + +/** + * Returns an operator that takes elements from the iterable while the predicate returns `true`. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/take-while/~/takeWhile takeWhile} for native takeWhile. + * + * @param fn The predicate to take elements with. + * @returns An operator that takes elements from the iterable while the predicate returns `true`. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { takeWhile } from "@core/iterutil/pipe/take-while"; + * + * const iter = pipe( + * [1, 2, 3, 4, 5], + * takeWhile((v) => v < 4), + * ); + * console.log(Array.from(iter)); // [1, 2, 3] + * ``` + */ +export function takeWhile( + fn: (value: T, index: number) => boolean, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, fn); +} diff --git a/pipe/take_while_test.ts b/pipe/take_while_test.ts new file mode 100644 index 0000000..8e75291 --- /dev/null +++ b/pipe/take_while_test.ts @@ -0,0 +1,13 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { takeWhile } from "./take_while.ts"; + +Deno.test("takeWhile", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3, 4, 5], takeWhile((v) => v < 4)); + const expected = [1, 2, 3]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/uniq.ts b/pipe/uniq.ts new file mode 100644 index 0000000..29a64e1 --- /dev/null +++ b/pipe/uniq.ts @@ -0,0 +1,30 @@ +import { uniq as base } from "@core/iterutil/uniq"; + +/** + * Returns an operator that yields the unique elements of the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/uniq/~/uniq uniq} for native uniq. + * + * @param identify An optional function to transform the elements before checking for uniqueness. + * @returns An operator that yields the unique elements of the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { uniq } from "@core/iterutil/pipe/uniq"; + * + * const iter1 = pipe([1, 2, 2, 3, 3, 3], uniq()); + * console.log(Array.from(iter1)); // [1, 2, 3] + * + * const iter2 = pipe( + * [1, 2, 3, 4, 5, 6, 7, 8, 9], + * uniq((v) => v % 4), + * ); + * console.log(Array.from(iter2)); // [1, 2, 3, 4] + * ``` + */ +export function uniq( + identify: (v: T, index: number) => unknown = (v) => v, +): (iterable: Iterable) => Iterable { + return (iterable) => base(iterable, identify); +} diff --git a/pipe/uniq_test.ts b/pipe/uniq_test.ts new file mode 100644 index 0000000..db7ffe9 --- /dev/null +++ b/pipe/uniq_test.ts @@ -0,0 +1,23 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { uniq } from "./uniq.ts"; + +Deno.test("uniq", async (t) => { + await t.step("usage 1", () => { + const result = pipe([1, 2, 2, 3, 3, 3], uniq()); + const expected = [1, 2, 3]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); + + await t.step("with identify", () => { + const result = pipe( + [1, 2, 3, 4, 5, 6, 7, 8, 9], + uniq((v) => v % 4), + ); + const expected = [1, 2, 3, 4]; + assertEquals(Array.from(result), expected); + assertType>>(true); + }); +}); diff --git a/pipe/zip.ts b/pipe/zip.ts new file mode 100644 index 0000000..e554c2c --- /dev/null +++ b/pipe/zip.ts @@ -0,0 +1,33 @@ +import { type Zip, zip as base } from "@core/iterutil/zip"; + +/** + * Returns an operator that zips the provided iterables with the iterable. + * + * See {@linkcode https://jsr.io/@core/iterutil/doc/zip/~/zip zip} for native zip. + * + * @param iterables The iterables to zip. + * @returns An operator that zips the provided iterables with the iterable. + * + * @example + * ```ts + * import { pipe } from "@core/pipe"; + * import { zip } from "@core/iterutil/pipe/zip"; + * + * const iter = pipe( + * [1, 2, 3], + * zip(["a", "b", "c"], [true, true, false]), + * ); + * console.log(Array.from(iter)); // [[1, "a", true], [2, "b", true], [3, "c", false]] + * ``` + */ +export function zip< + U extends readonly [ + Iterable, + ...Iterable[], + ], +>( + ...iterables: U +): (iterable: Iterable) => Iterable<[T, ...Zip]> { + return (iterable: Iterable) => + base(iterable, ...iterables) as Iterable<[T, ...Zip]>; +} diff --git a/pipe/zip_test.ts b/pipe/zip_test.ts new file mode 100644 index 0000000..ecd3dc4 --- /dev/null +++ b/pipe/zip_test.ts @@ -0,0 +1,15 @@ +import { assertEquals } from "@std/assert"; +import { assertType, type IsExact } from "@std/testing/types"; +import { pipe } from "@core/pipe"; +import { zip } from "./zip.ts"; + +Deno.test("zip", async (t) => { + await t.step("usage", () => { + const result = pipe([1, 2, 3], zip(["a", "b", "c"], [true, false, true])); + const expected = [[1, "a", true], [2, "b", false], [3, "c", true]]; + assertEquals(Array.from(result), expected); + assertType>>( + true, + ); + }); +});