Repository: marpple/FxTS
Pull request #112 (2021-12-23 03:18:07)

Change evaluation method to lazy evaluation on `reverse` function
why?
in `array` case, lazy evaluation makes sense.


Patch:
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index fe05d270..3376bd00 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -19,6 +19,7 @@ import prepend from "./prepend";
 import range from "./range";
 import reject from "./reject";
 import repeat from "./repeat";
+import reverse from "./reverse";
 import split from "./split";
 import take from "./take";
 import takeUntil from "./takeUntil";
@@ -52,6 +53,7 @@ export {
   range,
   reject,
   repeat,
+  reverse,
   split,
   take,
   takeUntil,
diff --git a/src/Lazy/reverse.ts b/src/Lazy/reverse.ts
new file mode 100644
index 00000000..24e05139
--- /dev/null
+++ b/src/Lazy/reverse.ts
@@ -0,0 +1,114 @@
+import toArray from "../toArray";
+import isArray from "../isArray";
+import isString from "../isString";
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
+import concurrent, { isConcurrent } from "./concurrent";
+
+function* stringReverse(str: string) {
+  const arr = Array.from(str);
+  for (let i = arr.length - 1; i >= 0; i--) {
+    yield arr[i];
+  }
+}
+
+function* arrayReverse<T>(arr: Array<T>) {
+  for (let i = arr.length - 1; i >= 0; i--) {
+    yield arr[i];
+  }
+}
+
+function* sync<T>(iterable: Iterable<T>) {
+  const arr = toArray(iterable);
+  for (let i = arr.length - 1; i >= 0; i--) {
+    yield arr[i];
+  }
+}
+
+async function* asyncSequential<T>(iterable: AsyncIterable<T>) {
+  const arr = await toArray(iterable);
+  for (let i = arr.length - 1; i >= 0; i--) {
+    yield arr[i];
+  }
+}
+
+function async<T>(iterable: AsyncIterable<T>): AsyncIterableIterator<T> {
+  let iterator: AsyncIterator<T>;
+  return {
+    [Symbol.asyncIterator]() {
+      return this;
+    },
+
+    async next(_concurrent: any) {
+      if (iterator === undefined) {
+        iterator = isConcurrent(_concurrent)
+          ? asyncSequential(concurrent(_concurrent.length, iterable))
+          : asyncSequential(iterable);
+      }
+
+      return iterator.next(_concurrent);
+    },
+  };
+}
+
+/**
+ * Returns Iterable/AsyncIterable of the given elements in reverse order.
+ * Note:Evaluates all Iterable, except for array, and returns a lazy-reversed iterator.
+ *
+ * @example
+ * ```ts
+ * const iter1 = reverse([1, 2, 3]);
+ * iter1.next(); // {value: 3, done: false}
+ * iter1.next(); // {value: 2, done: false}
+ * iter1.next(); // {value: 1, done: false}
+ * iter1.next(); // {value: undefined, done: true}
+ *
+ * const iter2 = reverse("abc");
+ * iter2.next(); // {value: "c", done: false}
+ * iter2.next(); // {value: "b", done: false}
+ * iter2.next(); // {value: "a", done: false}
+ * iter2.next(); // {value: undefined, done: true}
+ *
+ * // with pipe
+ * pipe(
+ *  [1, 2, 3, 4, 5],
+ *  reverse,
+ *  toArray,
+ * ); // [5, 4, 3, 2, 1]
+ *
+ * pipe(
+ *  "abcde",
+ *  reverse,
+ *  toArray,
+ * ); // "edcba"
+ * ```
+ *
+ * see {@link https://fxts.dev/docs/pipe | pipe} {@link https://fxts.dev/docs/toArray | toArray}
+ */
+function reverse<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable: T,
+): ReturnIterableIteratorType<T>;
+
+function reverse<T extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable: T,
+) {
+  if (isArray(iterable)) {
+    return arrayReverse(iterable);
+  }
+
+  if (isString(iterable)) {
+    return stringReverse(iterable);
+  }
+
+  if (isIterable(iterable)) {
+    return sync(iterable);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(iterable);
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default reverse;
diff --git a/src/index.ts b/src/index.ts
index 9fc0d51a..15e99394 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -34,7 +34,6 @@ import pickBy from "./pickBy";
 import pipe from "./pipe";
 import pipe1 from "./pipe1";
 import reduce from "./reduce";
-import reverse from "./reverse";
 import size from "./size";
 import some from "./some";
 import sort from "./sort";
@@ -81,7 +80,6 @@ export {
   pipe,
   pipe1,
   reduce,
-  reverse,
   size,
   some,
   sort,
diff --git a/src/reverse.ts b/src/reverse.ts
deleted file mode 100644
index f6e621c2..00000000
--- a/src/reverse.ts
+++ /dev/null
@@ -1,101 +0,0 @@
-import toArray from "./toArray";
-import isArray from "./isArray";
-import isString from "./isString";
-import IterableInfer from "./types/IterableInfer";
-import { isAsyncIterable, isIterable } from "./_internal/utils";
-
-type ReturnReverseType<
-  T extends Iterable<unknown> | AsyncIterable<unknown> | string,
-> = T extends string
-  ? string
-  : T extends Iterable<unknown>
-  ? IterableInfer<T>[]
-  : T extends AsyncIterable<unknown>
-  ? Promise<IterableInfer<T>[]>
-  : never;
-
-function stringReverse(str: string) {
-  const arr = Array.from(str);
-  let res = "";
-  for (let i = arr.length - 1; i >= 0; i--) {
-    res += arr[i];
-  }
-  return res;
-}
-
-function sync<T>(iterable: Iterable<T>) {
-  const arr = toArray(iterable);
-  const res = [];
-  for (let i = arr.length - 1; i >= 0; i--) {
-    res.push(arr[i]);
-  }
-  return res;
-}
-
-async function async<T>(iterable: AsyncIterable<T>) {
-  const arr = await toArray(iterable);
-  const res = [];
-  for (let i = arr.length - 1; i >= 0; i--) {
-    res.push(arr[i]);
-  }
-  return res;
-}
-
-/**
- * Returns the given elements in reverse order.
- *
- * @example
- * ```ts
- * reverse([1, 2, 3, 4, 5]); // [5, 4, 3, 2, 1]
- * reverse("abcde"); // "edcba"
- *
- * // with pipe
- * pipe(
- *  [1, 2, 3, 4, 5],
- *  reverse,
- * ); // [5, 4, 3, 2, 1]
- *
- * pipe(
- *  "abcde",
- *  reverse,
- * ); // "edcba"
- *
- * await pipe(
- *  [1, 2, 3, 4, 5],
- *  toAsync,
- *  reverse,
- * ); // [5, 4, 3, 2, 1]
- *
- * await pipe(
- *  "abcde",
- *  toAsync,
- *  reverse,
- * ); // ["e", "d", "c", "b", "a"]
- * ```
- */
-function reverse<T extends Iterable<unknown> | AsyncIterable<unknown> | string>(
-  iterable: T,
-): ReturnReverseType<T>;
-function reverse<T>(
-  iterable: string | number[] | Iterable<T> | AsyncIterable<T>,
-) {
-  if (isArray(iterable)) {
-    return iterable.reverse();
-  }
-
-  if (isString(iterable)) {
-    return stringReverse(iterable);
-  }
-
-  if (isIterable(iterable)) {
-    return sync(iterable);
-  }
-
-  if (isAsyncIterable(iterable)) {
-    return async(iterable);
-  }
-
-  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
-}
-
-export default reverse;


Test patch:
diff --git a/test/Lazy/reverse.spec.ts b/test/Lazy/reverse.spec.ts
new file mode 100644
index 00000000..bf4f0b29
--- /dev/null
+++ b/test/Lazy/reverse.spec.ts
@@ -0,0 +1,69 @@
+import { pipe, toAsync, reverse, toArray, filter, map, delay } from "../../src";
+import concurrent, { Concurrent } from "../../src/Lazy/concurrent";
+import { callFuncAfterTime, generatorMock } from "../utils";
+
+describe("reverse", function () {
+  describe("sync", function () {
+    it.each([
+      [
+        [1, 2, 3, 4],
+        [4, 3, 2, 1],
+      ],
+      ["abcd", ["d", "c", "b", "a"]],
+    ])(
+      "should return the given elements %s in reverse order %s",
+      function (input, result) {
+        expect(toArray(reverse(input))).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe([1, 2, 3, 4], reverse, toArray);
+      expect(res).toEqual([4, 3, 2, 1]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [
+        [1, 2, 3, 4],
+        [4, 3, 2, 1],
+      ],
+      ["abcd", ["d", "c", "b", "a"]],
+    ])(
+      "should return the given elements %s in reverse order %s",
+      async function (input, result) {
+        expect(await toArray(reverse(toAsync(input as any)))).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe([1, 2, 3, 4], toAsync, reverse, toArray);
+      expect(res).toEqual([4, 3, 2, 1]);
+    });
+
+    it("should be reversed elements concurrently", async function () {
+      const fn = jest.fn();
+      callFuncAfterTime(fn, 400);
+      const res = await pipe(
+        toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
+        map((a) => delay(100, a)),
+        filter((a) => a % 2 === 0),
+        reverse,
+        concurrent(3),
+        toArray,
+      );
+      expect(fn).toBeCalled();
+      expect(res).toEqual([10, 8, 6, 4, 2]);
+    }, 450);
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock = generatorMock();
+      const iter = reverse(mock);
+      const concurrent = Concurrent.of(2) as any;
+
+      await iter.next(concurrent);
+      expect((mock as any).getConcurrent()).toEqual(concurrent);
+    });
+  });
+});
diff --git a/test/reverse.spec.ts b/test/reverse.spec.ts
deleted file mode 100644
index 6524dda2..00000000
--- a/test/reverse.spec.ts
+++ /dev/null
@@ -1,43 +0,0 @@
-import { pipe, toAsync, reverse } from "../src";
-
-describe("reverse", function () {
-  describe("sync", function () {
-    it.each([
-      [
-        [1, 2, 3, 4],
-        [4, 3, 2, 1],
-      ],
-      ["abcd", "dcba"],
-    ])(
-      "should return the given elements %s in reverse order %s",
-      function (input, result) {
-        expect(reverse(input)).toEqual(result);
-      },
-    );
-
-    it("should be able to be used as a curried function in the pipeline", function () {
-      const res = pipe([1, 2, 3, 4], reverse);
-      expect(res).toEqual([4, 3, 2, 1]);
-    });
-  });
-
-  describe("async", function () {
-    it.each([
-      [
-        [1, 2, 3, 4],
-        [4, 3, 2, 1],
-      ],
-      ["abcd", ["d", "c", "b", "a"]],
-    ])(
-      "should return the given elements %s in reverse order %s",
-      async function (input, result) {
-        expect(await reverse(toAsync(input as any))).toEqual(result);
-      },
-    );
-
-    it("should be able to be used as a curried function in the pipeline", async function () {
-      const res = await pipe([1, 2, 3, 4], toAsync, reverse);
-      expect(res).toEqual([4, 3, 2, 1]);
-    });
-  });
-});
diff --git a/type-check/Lazy/reverse.test.ts b/type-check/Lazy/reverse.test.ts
new file mode 100644
index 00000000..621dcd19
--- /dev/null
+++ b/type-check/Lazy/reverse.test.ts
@@ -0,0 +1,24 @@
+import * as Test from "../../src/types/Test";
+import { reverse, pipe, toAsync } from "../../src";
+
+const { checks, check } = Test;
+
+const res1 = reverse([]);
+const res2 = reverse([1, 2, 3, 4]);
+const res3 = reverse("abcd");
+const res4 = reverse(toAsync([1, 2, 3, 4]));
+const res5 = reverse(toAsync("abcd"));
+const res6 = pipe([], reverse);
+const res7 = pipe(toAsync([1, 2, 3, 4]), reverse);
+const res8 = pipe(toAsync("abcd"), reverse);
+
+checks([
+  check<typeof res1, IterableIterator<never>, Test.Pass>(),
+  check<typeof res2, IterableIterator<number>, Test.Pass>(),
+  check<typeof res3, IterableIterator<string>, Test.Pass>(),
+  check<typeof res4, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res5, AsyncIterableIterator<string>, Test.Pass>(),
+  check<typeof res6, IterableIterator<never>, Test.Pass>(),
+  check<typeof res7, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res8, AsyncIterableIterator<string>, Test.Pass>(),
+]);
diff --git a/type-check/reverse.test.ts b/type-check/reverse.test.ts
deleted file mode 100644
index 780cb98e..00000000
--- a/type-check/reverse.test.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as Test from "../src/types/Test";
-import { reverse, pipe, toAsync } from "../src";
-
-const { checks, check } = Test;
-
-const res1 = reverse([]);
-const res2 = reverse([1, 2, 3, 4]);
-const res3 = reverse("abcd");
-const res4 = reverse(toAsync([1, 2, 3, 4]));
-const res5 = reverse(toAsync("abcd"));
-const res6 = pipe([], reverse);
-const res7 = pipe(toAsync([1, 2, 3, 4]), reverse);
-const res8 = pipe(toAsync("abcd"), reverse);
-
-checks([
-  check<typeof res1, never[], Test.Pass>(),
-  check<typeof res2, number[], Test.Pass>(),
-  check<typeof res3, string, Test.Pass>(),
-  check<typeof res4, Promise<number[]>, Test.Pass>(),
-  check<typeof res5, Promise<string[]>, Test.Pass>(),
-  check<typeof res6, never[], Test.Pass>(),
-  check<typeof res7, Promise<number[]>, Test.Pass>(),
-  check<typeof res8, Promise<string[]>, Test.Pass>(),
-]);