Repository: marpple/FxTS (ts)

Issue:
Add `intersection` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

Combines two `Iterable` or `AsyncIterable` into a set (i.e. no duplicates) composed of those elements common to both `Iterable/AsyncIterable`.


## 💻 Use Cases

<!-- What do you want to use this for? -->

```ts
const iter = intersection([1,2,3,4], [4,3,5,6]);
iter.next() // {value: 4, done:false};
iter.next() // {value: 3, done:false};
iter.next() // {value: undefined, done:true};
```


Pull request description:
feat: add `intersection` function
Fixes #81 


Patch:
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index 96cced49..03765a12 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -12,6 +12,7 @@ import dropUntil from "./dropUntil";
 import filter from "./filter";
 import flat from "./flat";
 import flatMap from "./flatMap";
+import intersection from "./intersection";
 import intersectionBy from "./intersectionBy";
 import map from "./map";
 import peek from "./peek";
@@ -20,7 +21,6 @@ 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";
@@ -47,6 +47,7 @@ export {
   filter,
   flat,
   flatMap,
+  intersection,
   intersectionBy,
   map,
   peek,
@@ -55,7 +56,6 @@ export {
   range,
   reject,
   repeat,
-  reverse,
   split,
   take,
   takeUntil,
diff --git a/src/Lazy/intersection.ts b/src/Lazy/intersection.ts
new file mode 100644
index 00000000..af1bb610
--- /dev/null
+++ b/src/Lazy/intersection.ts
@@ -0,0 +1,82 @@
+import identity from "../identity";
+import IterableInfer from "../types/IterableInfer";
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+import intersectionBy from "./intersectionBy";
+
+/**
+ * Returns Iterable/AsyncIterable of all elements in the `iterable2` contained in the `iterable1`.
+ *
+ * @example
+ * ```ts
+ * const iter = intersection([2, 1], [2, 3, 4]);
+ * iter.next(); // {value: 2, done:false}
+ * iter.next(); // {value: undefined, done: true}
+ * ```
+ */
+function intersection<T>(
+  iterable1: Iterable<T>,
+  iterable2: Iterable<T>,
+): IterableIterator<T>;
+
+function intersection<T>(
+  iterable1: AsyncIterable<T>,
+  iterable2: Iterable<T>,
+): AsyncIterableIterator<T>;
+
+function intersection<T>(
+  iterable1: Iterable<T>,
+  iterable2: AsyncIterable<T>,
+): AsyncIterableIterator<T>;
+
+function intersection<T>(
+  iterable1: AsyncIterable<T>,
+  iterable2: AsyncIterable<T>,
+): AsyncIterableIterator<T>;
+
+function intersection<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  B extends Iterable<unknown> | AsyncIterable<unknown>,
+>(
+  iterable1: A,
+): (
+  iterable2: B,
+) => A extends AsyncIterable<unknown>
+  ? AsyncIterableIterator<IterableInfer<B>>
+  : B extends AsyncIterable<unknown>
+  ? AsyncIterableIterator<IterableInfer<B>>
+  : IterableIterator<IterableInfer<B>>;
+
+function intersection<T>(
+  iterable1: Iterable<T> | AsyncIterable<T>,
+  iterable2?: Iterable<T> | AsyncIterable<T>,
+):
+  | IterableIterator<T>
+  | AsyncIterableIterator<T>
+  | ((
+      iterable2: Iterable<T> | AsyncIterable<T>,
+    ) => IterableIterator<T> | AsyncIterableIterator<T>) {
+  if (iterable2 === undefined) {
+    return (iterable2: any) => {
+      return intersection(iterable1 as any, iterable2);
+    };
+  }
+
+  if (isIterable(iterable1) && isIterable(iterable2)) {
+    return intersectionBy(identity, iterable1, iterable2);
+  }
+  if (isIterable(iterable1) && isAsyncIterable(iterable2)) {
+    return intersectionBy(identity, iterable1, iterable2);
+  }
+  if (isAsyncIterable(iterable1) && isIterable(iterable2)) {
+    return intersectionBy(identity, iterable1, iterable2);
+  }
+  if (isAsyncIterable(iterable1) && isAsyncIterable(iterable2)) {
+    return intersectionBy(identity, iterable1, iterable2);
+  }
+
+  throw new TypeError(
+    "'iterable1' and 'iterable2' must be type of Iterable or AsyncIterable",
+  );
+}
+
+export default intersection;
diff --git a/src/Lazy/reverse.ts b/src/Lazy/reverse.ts
deleted file mode 100644
index 24e05139..00000000
--- a/src/Lazy/reverse.ts
+++ /dev/null
@@ -1,114 +0,0 @@
-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/add.ts b/src/add.ts
deleted file mode 100644
index e0e280cc..00000000
--- a/src/add.ts
+++ /dev/null
@@ -1,81 +0,0 @@
-import isNumber from "./isNumber";
-import isString from "./isString";
-import Awaited from "./types/Awaited";
-
-// prettier-ignore
-type ReturnAddType<T, A extends T, B extends T> = 
-  | [A] extends [Promise<Awaited<T>>]
-  ? Promise<Awaited<T>>
-  : [B] extends [Promise<Awaited<T>>]
-  ? Promise<Awaited<T>>
-  : Awaited<T>;
-
-function sync(a: number | string, b: number | string) {
-  if (isNumber(a) && isNumber(b)) {
-    return a + b;
-  }
-
-  if (isString(a) && isString(b)) {
-    return a + b;
-  }
-
-  throw new TypeError("'a' or 'b' must be type of number or string");
-}
-
-async function async(a: Promise<number | string>, b: Promise<number | string>) {
-  return sync(await a, await b);
-}
-
-/**
- * Adds two values.
- *
- * @example
- * ```ts
- * add(1, 2); // 3
- * await add(1, Promise.resolve(2)); // 3
- * await add(Promise.resolve(1), 2); // 3
- * await add(Promise.resolve(1), Promise.resolve(2)); // 3
- *
- * add('a', 'b'); // 'ab'
- * await add('a', Promise.resolve('b')); // 'ab'
- * await add(Promise.resolve('a'), 'b'); // 'ab'
- * await add(Promise.resolve('a'), Promise.resolve('b')); // 'ab'
- * ```
- */
-function add<
-  A extends number | Promise<number>,
-  B extends number | Promise<number>,
->(a: A, b: B): ReturnAddType<number | Promise<number>, A, B>;
-function add<
-  A extends string | Promise<string>,
-  B extends string | Promise<string>,
->(a: A, b: B): ReturnAddType<string | Promise<string>, A, B>;
-
-function add<
-  A extends number | Promise<number>,
-  B extends number | Promise<number>,
->(a: A): (b: B) => ReturnAddType<number | Promise<number>, A, B>;
-
-function add<
-  A extends string | Promise<string>,
-  B extends string | Promise<string>,
->(a: A): (b: B) => ReturnAddType<string | Promise<string>, A, B>;
-
-function add<
-  A extends string | number | Promise<string | number>,
-  B extends string | number | Promise<string | number>,
->(a: A, b?: B): number | string | Promise<string | number> | ((b: B) => any) {
-  if (b === undefined) {
-    return (b: B) => {
-      return add(a as any, b as any);
-    };
-  }
-
-  if (a instanceof Promise || b instanceof Promise) {
-    return async(Promise.resolve(a), Promise.resolve(b));
-  }
-
-  return sync(a, b);
-}
-
-export default add;
diff --git a/src/index.ts b/src/index.ts
index 6aebb9b6..222cba20 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,3 @@
-import add from "./add";
 import apply from "./apply";
 import consume from "./consume";
 import countBy from "./countBy";
@@ -16,11 +15,9 @@ import indexBy from "./indexBy";
 import isArray from "./isArray";
 import isEmpty from "./isEmpty";
 import isNil from "./isNil";
-import isNumber from "./isNumber";
 import isObject from "./isObject";
 import isString from "./isString";
 import join from "./join";
-import juxt from "./juxt";
 import last from "./last";
 import max from "./max";
 import min from "./min";
@@ -35,15 +32,13 @@ import pickBy from "./pickBy";
 import pipe from "./pipe";
 import pipe1 from "./pipe1";
 import reduce from "./reduce";
-import size from "./size";
+import reverse from "./reverse";
 import some from "./some";
 import sort from "./sort";
-import sum from "./sum";
 import tap from "./tap";
 import toArray from "./toArray";
 
 export {
-  add,
   apply,
   consume,
   countBy,
@@ -63,11 +58,9 @@ export {
   isArray,
   isEmpty,
   isNil,
-  isNumber,
   isObject,
   isString,
   join,
-  juxt,
   last,
   max,
   min,
@@ -82,10 +75,9 @@ export {
   pipe,
   pipe1,
   reduce,
-  size,
+  reverse,
   some,
   sort,
-  sum,
   tap,
   toArray,
 };
diff --git a/src/isNumber.ts b/src/isNumber.ts
deleted file mode 100644
index a81e1019..00000000
--- a/src/isNumber.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-/**
- * Returns true if `n` is a Number.
- *
- * @example
- * ```ts
- * isNumber("a"); // true
- * isNumber(2); // false
- * ```
- */
-function isNumber(n: unknown): n is number {
-  return typeof n === "number";
-}
-
-export default isNumber;
diff --git a/src/juxt.ts b/src/juxt.ts
deleted file mode 100644
index 6326545c..00000000
--- a/src/juxt.ts
+++ /dev/null
@@ -1,57 +0,0 @@
-import type Append from "./types/Append";
-import type Arrow from "./types/Arrow";
-import type Cast from "./types/Cast";
-import type Tail from "./types/Tail";
-import type HasItem from "./types/HasItem";
-
-type ParameterTuples<FS extends Arrow[], T = []> = HasItem<FS> extends true
-  ? ParameterTuples<Tail<FS>, Append<Cast<T, any[]>, Parameters<FS[0]>>>
-  : T;
-
-type And<T extends any[], R = T[0]> = HasItem<T> extends true
-  ? And<Tail<T>, R & T[0]>
-  : R;
-
-type JuxtArgs<
-  FS extends Arrow[],
-  R = Cast<And<ParameterTuples<FS>>, any[]>,
-> = R extends never ? undefined : R;
-
-type ReturnTuples<FS extends Arrow[], T = []> = HasItem<FS> extends true
-  ? ReturnTuples<Tail<FS>, Append<Cast<T, any[]>, ReturnType<FS[0]>>>
-  : T;
-
-type JuxtReturnTypes<ARGS, FS extends Arrow[]> = ARGS extends undefined
-  ? void
-  : ReturnTuples<FS>;
-
-/**
- * `juxt` applies a list of functions to a list of values.
- *
- * @example
- * ```ts
- * const range = juxt([Math.min, Math.max], [1, 2, 3, 4]); // [1, 4]
- *
- * // with pipe
- * const entries = (obj: { a: number; b: number }) =>
- *   pipe(
- *     [Object.keys, Object.values] as const,
- *     juxt,
- *     (f) => f(obj),
- *     apply(zip),
- *     toArray,
- *   );
- *
- * entries({ a: 1, b: 2 }); // [ ["a", 1], ["b", 2] ]
- * ```
- *
- * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/apply | apply}
- */
-
-function juxt<FS extends Arrow[]>(
-  fs: [...FS] | readonly [...FS],
-): (...args: JuxtArgs<FS>) => JuxtReturnTypes<JuxtArgs<FS>, FS> {
-  return (...args: JuxtArgs<FS>) => fs.map((f: Arrow) => f(...args)) as any;
-}
-
-export default juxt;
diff --git a/src/reverse.ts b/src/reverse.ts
new file mode 100644
index 00000000..f6e621c2
--- /dev/null
+++ b/src/reverse.ts
@@ -0,0 +1,101 @@
+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;
diff --git a/src/size.ts b/src/size.ts
deleted file mode 100644
index 15ee83d7..00000000
--- a/src/size.ts
+++ /dev/null
@@ -1,45 +0,0 @@
-import each from "./each";
-import ReturnValueType from "./types/ReturnValueType";
-import { isAsyncIterable, isIterable } from "./_internal/utils";
-
-function sync<A extends Iterable<unknown>>(iterable: A) {
-  let a = 0;
-  each(() => a++, iterable);
-  return a;
-}
-
-async function async<A extends AsyncIterable<unknown>>(iterable: A) {
-  let a = 0;
-  await each(() => a++, iterable);
-  return a;
-}
-
-/**
- * Returns the size of Iterable/AsyncIterable
- *
- * @example
- * ```ts
- * size([1, 2, 3, 4]); // 4
- * size("abcde"); // 5
- *
- * await size(toAsync([1, 2, 3, 4])); // 4
- * ```
- */
-function size<A extends Iterable<unknown> | AsyncIterable<unknown>>(
-  iterable: A,
-): ReturnValueType<A, number>;
-
-function size<A extends Iterable<unknown> | AsyncIterable<unknown>>(
-  iterable: A,
-) {
-  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 size;
diff --git a/src/sum.ts b/src/sum.ts
deleted file mode 100644
index 4a6c800d..00000000
--- a/src/sum.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import add from "./add";
-import reduce from "./reduce";
-import ReturnValueType from "./types/ReturnValueType";
-import { isAsyncIterable, isIterable } from "./_internal/utils";
-
-/**
- * Adds all the elements of a Iterable/AsyncIterable.
- *
- * @example
- * ```ts
- * sum([1, 2, 3, 4]); // 10
- * sum(['a', 'b', 'c']); // 'abc'
- * await sum(toAsync([1, 2, 3, 4])); // 10
- * await sum(toAsync(['a', 'b', 'c'])); // 'abc'
- * ```
- */
-function sum<
-  A extends
-    | Iterable<number>
-    | AsyncIterable<number>
-    | Iterable<string>
-    | AsyncIterable<string>,
->(iterable: A): ReturnValueType<A> {
-  if (isIterable(iterable)) {
-    return reduce(add, iterable) as ReturnValueType<A>;
-  }
-
-  if (isAsyncIterable(iterable)) {
-    return reduce(add, iterable) as ReturnValueType<A>;
-  }
-
-  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
-}
-
-export default sum;
diff --git a/src/types/HasItem.ts b/src/types/HasItem.ts
deleted file mode 100644
index 9f2a1103..00000000
--- a/src/types/HasItem.ts
+++ /dev/null
@@ -1,3 +0,0 @@
-type HasItem<T extends any[]> = T extends [any, ...any] ? true : false;
-
-export default HasItem;


Test patch:
diff --git a/test/Lazy/intersection.spec.ts b/test/Lazy/intersection.spec.ts
new file mode 100644
index 00000000..a2802197
--- /dev/null
+++ b/test/Lazy/intersection.spec.ts
@@ -0,0 +1,82 @@
+import { delay, map, pipe, toArray, toAsync, intersection } from "../../src";
+import concurrent, { Concurrent } from "../../src/Lazy/concurrent";
+import { callFuncAfterTime, generatorMock } from "../utils";
+
+describe("intersection", function () {
+  describe("sync", function () {
+    it.each([
+      [
+        [1, 2, 3, 4],
+        [3, 4, 4, 5, 6],
+        [3, 4],
+      ],
+      ["abcd", "cdefgc", ["c", "d"]],
+    ])(
+      "should return all elements in %o contained %o",
+      function (iterable1, iterable2, result) {
+        const iter = intersection(iterable1 as any, iterable2 as any);
+        expect(toArray(iter)).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe([1, 2, 3, 4], intersection([2, 4, 5, 6]), toArray);
+      expect(res).toEqual([2, 4]);
+    });
+  });
+
+  describe("async", function () {
+    it.each([
+      [toAsync([1, 2, 3, 4]), toAsync([3, 4, 4, 5, 6, 3]), [3, 4]],
+      [[1, 2, 3, 4], toAsync([3, 4, 4, 5, 6]), [3, 4]],
+      [toAsync([1, 2, 3, 4]), [3, 4, 4, 5, 6], [3, 4]],
+      ["abcd", "cdefg", ["c", "d"]],
+    ])(
+      "should return all elements in iterable1 contained iterable2",
+      async function (iterable1, iterable2, result) {
+        const iter = intersection(
+          iterable1 as AsyncIterable<any>,
+          iterable2 as AsyncIterable<any>,
+        );
+        expect(await toArray(iter)).toEqual(result);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe(
+        toAsync([1, 2, 3, 4]),
+        intersection([2, 4, 5, 6]),
+        toArray,
+      );
+      expect(res).toEqual([2, 4]);
+    });
+
+    it("should be handled concurrently", async function () {
+      const fn = jest.fn();
+      callFuncAfterTime(fn, 900);
+
+      const res = await pipe(
+        [1, 2, 3, 4, 5, 6, 7, 8, 9],
+        toAsync,
+        map((a) => delay(300, a)),
+        intersection([3, 4, 5]),
+        concurrent(3),
+        toArray,
+      );
+
+      expect(fn).toBeCalled();
+      expect(res).toEqual([3, 4, 5]);
+    }, 950);
+
+    it("should be passed concurrent object when job works concurrently", async function () {
+      const mock1 = generatorMock();
+      const mock2 = generatorMock();
+      const iter = intersection(mock1, mock2);
+      const concurrent = Concurrent.of(2) as any;
+
+      await iter.next(concurrent);
+      // mock2 only concurrent
+      expect((mock2 as any).getConcurrent()).toEqual(concurrent);
+    });
+  });
+});
diff --git a/test/Lazy/intersectionBy.spec.ts b/test/Lazy/intersectionBy.spec.ts
index b9375869..1364f915 100644
--- a/test/Lazy/intersectionBy.spec.ts
+++ b/test/Lazy/intersectionBy.spec.ts
@@ -1,5 +1,6 @@
-import { toArray, toAsync, intersectionBy } from "../../src";
+import { toArray, toAsync } from "../../src";
 import { Concurrent } from "../../src/Lazy/concurrent";
+import intersectionBy from "../../src/Lazy/intersectionBy";
 import { generatorMock } from "../utils";
 
 describe("intersectionBy", function () {
diff --git a/test/Lazy/reverse.spec.ts b/test/Lazy/reverse.spec.ts
deleted file mode 100644
index bf4f0b29..00000000
--- a/test/Lazy/reverse.spec.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-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/add.spec.ts b/test/add.spec.ts
deleted file mode 100644
index ce3441fe..00000000
--- a/test/add.spec.ts
+++ /dev/null
@@ -1,39 +0,0 @@
-import { add, pipe } from "../src";
-
-describe("add", function () {
-  describe("sync", function () {
-    it.each([
-      [1, 2, 3],
-      ["a", "b", "ab"],
-    ])("should add two values %s %s", function (a, b, result) {
-      expect(add(a as any, b as any)).toEqual(result);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", function () {
-      const res1 = pipe(1, add(2));
-      expect(res1).toEqual(3);
-      const res2 = pipe("b", add("a"));
-      expect(res2).toEqual("ab");
-    });
-  });
-
-  describe("async", function () {
-    it.each([
-      [Promise.resolve(1), 2, 3],
-      [1, Promise.resolve(2), 3],
-      [Promise.resolve(1), Promise.resolve(2), 3],
-      [Promise.resolve("a"), "b", "ab"],
-      ["a", Promise.resolve("b"), "ab"],
-      [Promise.resolve("a"), Promise.resolve("b"), "ab"],
-    ])("should add two values %s %s", async function (a, b, result) {
-      expect(await add(a as Promise<any>, b as Promise<any>)).toEqual(result);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", async function () {
-      const res1 = await pipe(1, add(Promise.resolve(2)));
-      expect(res1).toEqual(3);
-      const res2 = await pipe("b", add(Promise.resolve("a")));
-      expect(res2).toEqual("ab");
-    });
-  });
-});
diff --git a/test/isNumber.spec.ts b/test/isNumber.spec.ts
deleted file mode 100644
index 706d114f..00000000
--- a/test/isNumber.spec.ts
+++ /dev/null
@@ -1,16 +0,0 @@
-import { isNumber } from "../src/index";
-
-describe("isNumber", function () {
-  it.each([undefined, null, true, "1", Symbol("a"), () => null])(
-    "given non number then should return false",
-    function (s) {
-      const result = isNumber(s);
-      expect(result).toEqual(false);
-    },
-  );
-
-  it("given number then should return true", function () {
-    const result = isNumber(2);
-    expect(result).toEqual(true);
-  });
-});
diff --git a/test/juxt.spec.ts b/test/juxt.spec.ts
deleted file mode 100644
index 6ada48d9..00000000
--- a/test/juxt.spec.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { apply, juxt, pipe, toArray, zip } from "../src";
-
-describe("juxt", function () {
-  it("should return an empty array when the list of functions is absent", function () {
-    const res = juxt([])();
-    expect(res).toEqual([]);
-  });
-
-  it("should apply a list of functions to a list of values", function () {
-    const res = juxt([Math.min, Math.max])(1, 2, 3, -4, 5, 6, 7);
-    expect(res).toEqual([-4, 7]);
-  });
-
-  it("should be able to be used as a curried function in the pipeline", function () {
-    const entries = (obj: { a: number; b: number }) =>
-      pipe(
-        [Object.keys, Object.values] as const,
-        juxt,
-        (f) => f(obj),
-        apply(zip),
-        toArray,
-      );
-    const res = entries({ a: 1, b: 2 });
-    expect(res).toEqual([
-      ["a", 1],
-      ["b", 2],
-    ]);
-  });
-});
diff --git a/test/reverse.spec.ts b/test/reverse.spec.ts
new file mode 100644
index 00000000..6524dda2
--- /dev/null
+++ b/test/reverse.spec.ts
@@ -0,0 +1,43 @@
+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/test/size.spec.ts b/test/size.spec.ts
deleted file mode 100644
index 89a78a1a..00000000
--- a/test/size.spec.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { size, toAsync } from "../src";
-
-describe("size", function () {
-  describe("sync", function () {
-    it.each([
-      [[1, 2, 3, 4, 5], 5],
-      ["abcdef", 6],
-    ])("should return the size of elements %s", function (iterable, result) {
-      expect(size(iterable)).toEqual(result);
-    });
-  });
-
-  describe("async", function () {
-    it.each([
-      [[1, 2, 3, 4, 5], 5],
-      ["abcdef", 6],
-    ])(
-      "should return the size of elements %s",
-      async function (iterable, result) {
-        expect(await size(toAsync(iterable as any[]))).toEqual(result);
-      },
-    );
-  });
-});
diff --git a/test/sum.spec.ts b/test/sum.spec.ts
deleted file mode 100644
index 0ff1d911..00000000
--- a/test/sum.spec.ts
+++ /dev/null
@@ -1,35 +0,0 @@
-import { sum, pipe, toAsync } from "../src";
-
-describe("sum", function () {
-  describe("sync", function () {
-    it.each([
-      [[1, 2, 3], 6],
-      [["a", "b", "c"], "abc"],
-    ])("should sum all elements %s %s", function (iterable, result) {
-      expect(sum(iterable as Iterable<any>)).toEqual(result);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", function () {
-      const res1 = pipe([1, 2, 3], sum);
-      expect(res1).toEqual(6);
-      const res2 = pipe(["a", "b", "c"], sum);
-      expect(res2).toEqual("abc");
-    });
-  });
-
-  describe("async", function () {
-    it.each([
-      [[1, 2, 3], 6],
-      [["a", "b", "c"], "abc"],
-    ])("should sum all elements %s %s", async function (iterable, result) {
-      expect(await sum(toAsync(iterable as Iterable<any>))).toEqual(result);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", async function () {
-      const res1 = await pipe([1, 2, 3], toAsync, sum);
-      expect(res1).toEqual(6);
-      const res2 = await pipe(["a", "b", "c"], toAsync, sum);
-      expect(res2).toEqual("abc");
-    });
-  });
-});
diff --git a/type-check/Lazy/intersection.test.ts b/type-check/Lazy/intersection.test.ts
new file mode 100644
index 00000000..2cae8c39
--- /dev/null
+++ b/type-check/Lazy/intersection.test.ts
@@ -0,0 +1,20 @@
+import * as Test from "../../src/types/Test";
+import { intersection, pipe, toArray, toAsync } from "../../src";
+
+const { checks, check } = Test;
+
+const res1 = intersection([1, 2, 3, 4], [5, 6, 3, 4]);
+const res2 = intersection(toAsync([1, 2, 3, 4]), [5, 6, 3, 4]);
+const res3 = intersection([1, 2, 3, 4], toAsync([5, 6, 3, 4]));
+const res4 = intersection(toAsync([1, 2, 3, 4]), toAsync([5, 6, 3, 4]));
+const res5 = pipe([1, 2, 3, 4], intersection([5, 6, 7, 8]), toArray);
+const res6 = pipe([1, 2, 3, 4], intersection("abcd"), toArray);
+
+checks([
+  check<typeof res1, IterableIterator<number>, Test.Pass>(),
+  check<typeof res2, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res3, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res4, AsyncIterableIterator<number>, Test.Pass>(),
+  check<typeof res5, number[], Test.Pass>(),
+  check<typeof res6, number[], Test.Pass>(),
+]);
diff --git a/type-check/Lazy/reverse.test.ts b/type-check/Lazy/reverse.test.ts
deleted file mode 100644
index 621dcd19..00000000
--- a/type-check/Lazy/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, 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/add.test.ts b/type-check/add.test.ts
deleted file mode 100644
index a931653d..00000000
--- a/type-check/add.test.ts
+++ /dev/null
@@ -1,36 +0,0 @@
-import * as Test from "../src/types/Test";
-import { add, pipe } from "../src";
-
-const { checks, check } = Test;
-
-const res1 = add(1, 2);
-const res2 = add("a", "b");
-const res3 = add(Promise.resolve(1), 2);
-const res4 = add(1, Promise.resolve(2));
-const res5 = add(Promise.resolve(1), Promise.resolve(2));
-const res6 = add(Promise.resolve("a"), "b");
-const res7 = add("a", Promise.resolve("b"));
-const res8 = add(Promise.resolve("a"), Promise.resolve("b"));
-const res9 = pipe(2, add(1));
-const res10 = pipe("b", add("a"));
-const res11 = pipe(Promise.resolve(2), add(1));
-const res12 = pipe(Promise.resolve("b"), add("a"));
-const res13 = add(2)(3);
-const res14 = add("a")("b");
-
-checks([
-  check<typeof res1, number, Test.Pass>(),
-  check<typeof res2, string, Test.Pass>(),
-  check<typeof res3, Promise<number>, Test.Pass>(),
-  check<typeof res4, Promise<number>, Test.Pass>(),
-  check<typeof res5, Promise<number>, Test.Pass>(),
-  check<typeof res6, Promise<string>, Test.Pass>(),
-  check<typeof res7, Promise<string>, Test.Pass>(),
-  check<typeof res8, Promise<string>, Test.Pass>(),
-  check<typeof res9, number, Test.Pass>(),
-  check<typeof res10, string, Test.Pass>(),
-  check<typeof res11, Promise<number>, Test.Pass>(),
-  check<typeof res12, Promise<string>, Test.Pass>(),
-  check<typeof res13, number, Test.Pass>(),
-  check<typeof res14, string, Test.Pass>(),
-]);
diff --git a/type-check/isNumber.test.ts b/type-check/isNumber.test.ts
deleted file mode 100644
index 751d82e8..00000000
--- a/type-check/isNumber.test.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as Test from "../src/types/Test";
-import { isNumber, pipe } from "../src";
-
-const { checks, check } = Test;
-
-const res1 = isNumber(1);
-const res2 = isNumber("a");
-const res3 = pipe(1, isNumber);
-
-checks([
-  check<typeof res1, boolean, Test.Pass>(),
-  check<typeof res2, boolean, Test.Pass>(),
-  check<typeof res3, boolean, Test.Pass>(),
-]);
diff --git a/type-check/juxt.test.ts b/type-check/juxt.test.ts
deleted file mode 100644
index 8069ecd4..00000000
--- a/type-check/juxt.test.ts
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as Test from "../src/types/Test";
-import { apply, pipe, juxt } from "../src";
-
-const { checks, check } = Test;
-
-declare function f1(a: number, b: string): string;
-declare function f2(a: number, b: string): boolean;
-// declare function f3(a: number, b: boolean): number;
-// declare function f4(a: number, b: string, c: boolean): number;
-
-const res1 = juxt([])();
-const res2 = juxt([f1, f2])(1, "2");
-
-// pipeline
-const res3 = pipe([1, 2, 3, 4], apply(juxt([Math.max, Math.min])));
-const res4 = pipe(juxt([f1, f2]), (f) => f(1, "2"));
-const res5 = pipe([f1, f2] as const, juxt, (f) => f(1, "2"));
-const res6 = pipe([f1, f2] as [typeof f1, typeof f2], juxt, (f) => f(1, "2"));
-
-/* error
-const res7 = pipe([f2, f3] as const, juxt, (f) => f());
-const res8 = pipe([f2, f3], juxt, (f) => f(1, "2"));
-const res9 = pipe([f2, f4], juxt, (f) => f(1, "2"));
-const res10 = pipe([f2, f4] as const, juxt, (f) => f(1, "2", true));
-*/
-
-checks([
-  check<typeof res1, [], Test.Pass>(),
-  check<typeof res2, [string, boolean], Test.Pass>(),
-  check<typeof res3, [number, number], Test.Pass>(),
-  check<typeof res4, [string, boolean], Test.Pass>(),
-  check<typeof res5, [string, boolean], Test.Pass>(),
-  check<typeof res6, [string, boolean], Test.Pass>(),
-]);
diff --git a/type-check/reverse.test.ts b/type-check/reverse.test.ts
new file mode 100644
index 00000000..780cb98e
--- /dev/null
+++ b/type-check/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, 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>(),
+]);
diff --git a/type-check/size.test.ts b/type-check/size.test.ts
deleted file mode 100644
index 609476f5..00000000
--- a/type-check/size.test.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import * as Test from "../src/types/Test";
-import { size, pipe, toAsync } from "../src";
-
-const { checks, check } = Test;
-
-const res1 = size([]);
-const res2 = size([1, 2, 3, 4, 5]);
-const res3 = size(["a", "b", "c", "d"]);
-const res4 = size(toAsync(["a", "b", "c", "d"]));
-const res5 = pipe([1, 2, 3, 4, 5], size);
-const res6 = pipe(["a", "b", "c", "d"], toAsync, size);
-
-checks([
-  check<typeof res1, number, Test.Pass>(),
-  check<typeof res2, number, Test.Pass>(),
-  check<typeof res3, number, Test.Pass>(),
-  check<typeof res4, Promise<number>, Test.Pass>(),
-  check<typeof res5, number, Test.Pass>(),
-  check<typeof res6, Promise<number>, Test.Pass>(),
-]);
diff --git a/type-check/sum.test.ts b/type-check/sum.test.ts
deleted file mode 100644
index fe536315..00000000
--- a/type-check/sum.test.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import * as Test from "../src/types/Test";
-import { sum, pipe, toAsync } from "../src";
-
-const { checks, check } = Test;
-
-// const res0 = sum([1, 'a']); // error
-const res1 = sum([1, 2]);
-const res2 = sum(["a", "b"]);
-const res3 = sum(toAsync([1, 2]));
-const res4 = pipe([1, 2], sum);
-const res5 = pipe(["a", "b"], sum);
-const res6 = pipe([1, 2], toAsync, sum);
-const res7 = pipe(["a", "b"], toAsync, sum);
-
-checks([
-  check<typeof res1, number, Test.Pass>(),
-  check<typeof res2, string, Test.Pass>(),
-  check<typeof res3, Promise<number>, Test.Pass>(),
-  check<typeof res4, number, Test.Pass>(),
-  check<typeof res5, string, Test.Pass>(),
-  check<typeof res6, Promise<number>, Test.Pass>(),
-  check<typeof res7, Promise<string>, Test.Pass>(),
-]);