Repository: marpple/FxTS (ts)

Issue:
Add `pluck` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

Returns Iterable/AsyncIterable by plucking the same named property off all objects in Iterable/AsyncIterable supplied.


## 💻 Use Cases

<!-- What do you want to use this for? -->

```ts
const iter = pluck('age', [{age:21}, {age:22}, {age:23}]);
iter.next(); // 21
iter.next(); // 22
iter.next(); // 23

```


Pull request description:
feat: add `pluck` function
Fixes #28 


Patch:
diff --git a/src/Lazy/dropUntil.ts b/src/Lazy/dropUntil.ts
deleted file mode 100644
index 5c12d08e..00000000
--- a/src/Lazy/dropUntil.ts
+++ /dev/null
@@ -1,150 +0,0 @@
-import IterableInfer from "../types/IterableInfer";
-import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
-import { AsyncFunctionException } from "../_internal/error";
-import { isAsyncIterable, isIterable } from "../_internal/utils";
-import concurrent, { isConcurrent } from "./concurrent";
-
-function* sync<A, B>(f: (a: A) => B, iterable: Iterable<A>) {
-  const iterator = iterable[Symbol.iterator]();
-  const iterableIterator = {
-    [Symbol.iterator]() {
-      return iterator;
-    },
-  };
-
-  for (const a of iterableIterator) {
-    const res = f(a);
-    if (res instanceof Promise) {
-      throw new AsyncFunctionException();
-    }
-
-    if (res) {
-      yield* iterableIterator;
-    }
-  }
-}
-
-async function* asyncSequential<A, B>(
-  f: (a: A) => B,
-  iterable: AsyncIterable<A>,
-) {
-  const iterator = iterable[Symbol.asyncIterator]();
-  const iterableIterator = {
-    [Symbol.asyncIterator]() {
-      return iterator;
-    },
-  };
-
-  for await (const a of iterableIterator) {
-    if (await f(a)) {
-      yield* iterableIterator;
-    }
-  }
-}
-
-function async<A, B>(
-  f: (a: A) => B,
-  iterable: AsyncIterable<A>,
-): AsyncIterableIterator<A> {
-  let iterator: AsyncIterator<A>;
-  return {
-    [Symbol.asyncIterator]() {
-      return this;
-    },
-
-    async next(_concurrent: any) {
-      if (iterator === undefined) {
-        iterator = isConcurrent(_concurrent)
-          ? asyncSequential(f, concurrent(_concurrent.length, iterable))
-          : asyncSequential(f, iterable);
-      }
-
-      return iterator.next(_concurrent);
-    },
-  };
-}
-
-/**
- * Returns Iterable/AsyncIterable excluding elements dropped from the beginning.
- * Elements are deleted until the value applied to f returns truly. (It is deleted including the first value applied as true)
- *
- * @example
- * ```ts
- * const iter = dropUntil((a) => a < 3, [1, 2, 3, 4, 5, 1, 2]);
- * iter.next(); // {done:false, value: 3}
- * iter.next(); // {done:false, value: 4}
- * iter.next(); // {done:false, value: 5}
- *
- * // with pipe
- * pipe(
- *  [1, 2, 3, 4, 5, 1, 2],
- *  dropUntil((a) => a > 3),
- *  toArray,
- * ); // [5, 1, 2]
- *
- * await pipe(
- *  Promise.resolve([1, 2, 3, 4, 5, 1, 2]),
- *  dropUntil((a) => a > 3),
- *  toArray,
- * ); // [5, 1, 2]
- *
- * // if you want to use asynchronous callback
- * await pipe(
- *  Promise.resolve([1, 2, 3, 4, 5, 1, 2]),
- *  toAsync,
- *  dropUntil(async (a) => a > 3),
- *  toArray,
- * ); // [5, 1, 2]
- *
- * // with toAsync
- * await pipe(
- *  [Promise.resolve(1), Promise.resolve(2), Promise.resolve(3), Promise.resolve(4),
- * Promise.resolve(5), Promise.resolve(1), Promise.resolve(2)],
- *  toAsync,
- *  dropUntil((a) => a > 3),
- *  toArray,
- * ); // [5, 1, 2]
- *
- * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync},
- * {@link https://fxts.dev/docs/toArray | toArray}
- * ```
- */
-function dropUntil<A, B = unknown>(
-  f: (a: A) => B,
-  iterable: Iterable<A>,
-): IterableIterator<A>;
-
-function dropUntil<A, B = unknown>(
-  f: (a: A) => B,
-  iterable: AsyncIterable<A>,
-): AsyncIterableIterator<A>;
-
-function dropUntil<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
-  f: (a: IterableInfer<A>) => B,
-): (iterable: A) => ReturnIterableIteratorType<A>;
-
-function dropUntil<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
-  f: (a: IterableInfer<A>) => B,
-  iterable?: A,
-):
-  | IterableIterator<IterableInfer<A>>
-  | AsyncIterableIterator<IterableInfer<A>>
-  | ((iterable: A) => ReturnIterableIteratorType<A>) {
-  if (iterable === undefined) {
-    return (iterable: A) => {
-      return dropUntil(f, iterable as any) as ReturnIterableIteratorType<A>;
-    };
-  }
-
-  if (isIterable(iterable)) {
-    return sync(f, iterable as Iterable<IterableInfer<A>>);
-  }
-
-  if (isAsyncIterable(iterable)) {
-    return async(f, iterable as AsyncIterable<IterableInfer<A>>);
-  }
-
-  throw new TypeError("iterable must be type of Iterable or AsyncIterable");
-}
-
-export default dropUntil;
diff --git a/src/Lazy/index.ts b/src/Lazy/index.ts
index 2bc3fff9..e875434f 100644
--- a/src/Lazy/index.ts
+++ b/src/Lazy/index.ts
@@ -5,16 +5,15 @@ import concat from "./concat";
 import concurrent from "./concurrent";
 import drop from "./drop";
 import dropWhile from "./dropWhile";
-import dropUntil from "./dropUntil";
 import filter from "./filter";
 import flat from "./flat";
 import flatMap from "./flatMap";
 import map from "./map";
 import peek from "./peek";
+import pluck from "./pluck";
 import prepend from "./prepend";
 import range from "./range";
 import reject from "./reject";
-import split from "./split";
 import take from "./take";
 import takeUntil from "./takeUntil";
 import takeWhile from "./takeWhile";
@@ -32,16 +31,15 @@ export {
   concurrent,
   drop,
   dropWhile,
-  dropUntil,
   filter,
   flat,
   flatMap,
   map,
   peek,
+  pluck,
   prepend,
   range,
   reject,
-  split,
   take,
   takeUntil,
   takeWhile,
diff --git a/src/Lazy/pluck.ts b/src/Lazy/pluck.ts
new file mode 100644
index 00000000..8c0831a5
--- /dev/null
+++ b/src/Lazy/pluck.ts
@@ -0,0 +1,85 @@
+import { map } from ".";
+import { isAsyncIterable, isIterable } from "../_internal/utils";
+
+/**
+ * Returns Iterable/AsyncIterable by plucking the same named property off all objects in Iterable/AsyncIterable supplied.
+ *
+ * @example
+ * ```ts
+ * const iter = pluck('age', [{age:21}, {age:22}, {age:23}]);
+ * iter.next(); // {done:false, value: 21}
+ * iter.next(); // {done:false, value: 22}
+ * iter.next(); // {done:false, value: 23}
+ *
+ * // with pipe
+ * pipe(
+ *  [{age:21}, {age:22}, {age:23}],
+ *  pluck('age'),
+ *  toArray,
+ * ); // [21, 22 ,23]
+ *
+ * // if you want to use asynchronous callback
+ * await pipe(
+ *  Promise.resolve([{age:21}, {age:22}, {age:23}]),
+ *  toAsync,
+ *  pluck('age'),
+ *  toArray,
+ * ); // [21, 22 ,23]
+ *
+ * // with toAsync
+ * await pipe(
+ *  [Promise.resolve({age:21}), Promise.resolve({age:22}), Promise.resolve({age:23})],
+ *  toAsync,
+ *  pluck('age'),
+ *  toArray,
+ * ); // [21, 22 ,23]
+ * ```
+ * see {@link https://fxts.dev/docs/pipe | pipe}, {@link https://fxts.dev/docs/toAsync | toAsync},
+ * {@link https://fxts.dev/docs/toArray | toArray}
+ */
+function pluck<O extends object, K extends keyof O>(
+  key: K,
+  iterable: Iterable<O>,
+): IterableIterator<O[K]>;
+
+function pluck<O extends object, K extends keyof O>(
+  key: K,
+  iterable: AsyncIterable<O>,
+): AsyncIterableIterator<O[K]>;
+
+function pluck<O extends object, K extends keyof O>(
+  key: K,
+): (iterable: Iterable<O>) => IterableIterator<O[K]>;
+
+function pluck<O extends object, K extends keyof O>(
+  key: K,
+): (iterable: AsyncIterable<O>) => AsyncIterableIterator<O[K]>;
+
+function pluck<O extends object, K extends keyof O>(
+  key: K,
+  iterable?: Iterable<O> | AsyncIterable<O>,
+):
+  | IterableIterator<O[K]>
+  | AsyncIterableIterator<O[K]>
+  | ((iterable: Iterable<O>) => IterableIterator<O[K]>)
+  | ((iterable: AsyncIterable<O>) => AsyncIterableIterator<O[K]>) {
+  if (iterable === undefined) {
+    return (iterable: any): any => {
+      return pluck(key, iterable) as
+        | IterableIterator<O[K]>
+        | AsyncIterableIterator<O[K]>;
+    };
+  }
+
+  if (isIterable(iterable)) {
+    return map((a) => a[key], iterable);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return map((a) => a[key], iterable);
+  }
+
+  throw new TypeError("iterable must be type of Iterable or AsyncIterable");
+}
+
+export default pluck;
diff --git a/src/Lazy/split.ts b/src/Lazy/split.ts
deleted file mode 100644
index 907442df..00000000
--- a/src/Lazy/split.ts
+++ /dev/null
@@ -1,113 +0,0 @@
-import ReturnIterableIteratorType from "../types/ReturnIterableIteratorType";
-import { isAsyncIterable, isIterable } from "../_internal/utils";
-import concurrent, { isConcurrent } from "./concurrent";
-
-function* sync(sep: string, iterable: Iterable<string>) {
-  if (sep === "") {
-    return yield* iterable;
-  }
-
-  let acc = "";
-  let chr = "";
-  for (chr of iterable) {
-    if (chr === sep) {
-      yield acc;
-      acc = "";
-    } else {
-      acc += chr;
-    }
-  }
-
-  if (chr === sep) {
-    yield "";
-  } else if (acc.length > 0) {
-    yield acc;
-  }
-}
-
-async function* asyncSequential(sep: string, iterable: AsyncIterable<string>) {
-  if (sep === "") {
-    return yield* iterable;
-  }
-
-  let acc = "";
-  let chr = "";
-
-  for await (chr of iterable) {
-    if (chr === sep) {
-      yield acc;
-      acc = "";
-    } else {
-      acc += chr;
-    }
-  }
-
-  if (chr === sep) {
-    yield "";
-  } else if (acc.length > 0) {
-    yield acc;
-  }
-}
-
-function async(
-  sep: string,
-  iterable: AsyncIterable<string>,
-): AsyncIterableIterator<string> {
-  let _iterator: AsyncIterator<string>;
-  return {
-    async next(_concurrent: any) {
-      if (_iterator === undefined) {
-        _iterator = isConcurrent(_concurrent)
-          ? asyncSequential(sep, concurrent(_concurrent.length, iterable))
-          : asyncSequential(sep, iterable);
-      }
-      return _iterator.next(_concurrent);
-    },
-    [Symbol.asyncIterator]() {
-      return this;
-    },
-  };
-}
-
-function split(
-  sep: string,
-  iterable: Iterable<string>,
-): IterableIterator<string>;
-
-function split(
-  sep: string,
-  iterable: AsyncIterable<string>,
-): AsyncIterableIterator<string>;
-
-function split<A extends Iterable<string> | AsyncIterable<string>>(
-  sep: string,
-): (iterable: A) => ReturnIterableIteratorType<A, string>;
-
-function split<A extends Iterable<string> | AsyncIterable<string>>(
-  sep: string,
-  iterable?: A,
-):
-  | IterableIterator<string>
-  | AsyncIterableIterator<string>
-  | ((iterable: A) => ReturnIterableIteratorType<A, string>) {
-  if (iterable === undefined) {
-    return (iterable: A): ReturnIterableIteratorType<A, string> => {
-      return split(sep, iterable as any) as ReturnIterableIteratorType<
-        A,
-        string
-      >;
-    };
-  }
-
-  if (isIterable(iterable)) {
-    return sync(sep, iterable as Iterable<string>);
-  }
-
-  if (isAsyncIterable(iterable)) {
-    return async(sep, iterable as AsyncIterable<string>);
-  }
-
-  throw new TypeError("iterable must be type of Iterable or AsyncIterable");
-}
-
-export default split;


Test patch:
diff --git a/test/Lazy/dropUntil.spec.ts b/test/Lazy/dropUntil.spec.ts
deleted file mode 100644
index a458d859..00000000
--- a/test/Lazy/dropUntil.spec.ts
+++ /dev/null
@@ -1,175 +0,0 @@
-import {
-  concurrent,
-  delay,
-  dropUntil,
-  filter,
-  map,
-  pipe,
-  toArray,
-  toAsync,
-} from "../../src";
-import { Concurrent } from "../../src/Lazy/concurrent";
-import { AsyncFunctionException } from "../../src/_internal/error";
-import { callFuncAfterTime, generatorMock } from "../utils";
-
-describe("dropUntil", function () {
-  describe("sync", function () {
-    it("should be dropped elements until the value applied to callback returns truly", function () {
-      const arr = [1, 2, 3, 4, 5, 1, 2];
-      const acc = [];
-      for (const a of dropUntil((a) => a > 3, arr)) {
-        acc.push(a);
-      }
-      expect(acc).toEqual([5, 1, 2]);
-    });
-
-    it("should throw an error when the callback is asynchronous", function () {
-      const res = () => [...dropUntil(async (a) => a > 5, [1, 2, 3, 4, 5])];
-      expect(res).toThrowError(new AsyncFunctionException());
-    });
-
-    it("should be able to be used as a curried function in the pipeline", function () {
-      const res = pipe(
-        [1, 2, 3, 4, 5, 1, 2],
-        map((a) => a + 10),
-        filter((a) => a % 2 === 1),
-        dropUntil((a) => a > 13),
-        toArray,
-      );
-
-      expect(res).toEqual([11]);
-    });
-  });
-
-  describe("async", function () {
-    it("should be dropped elements until the value applied to callback returns truly", async function () {
-      const arr = [1, 2, 3, 4, 5, 1, 2];
-      const acc = [];
-      for await (const a of dropUntil((a) => a > 3, toAsync(arr))) {
-        acc.push(a);
-      }
-      expect(acc).toEqual([5, 1, 2]);
-    });
-
-    it("should be dropped elements until the value applied to async callback returns truly", async function () {
-      const arr = [1, 2, 3, 4, 5, 1, 2];
-      const acc = [];
-      for await (const a of dropUntil(async (a) => a > 3, toAsync(arr))) {
-        acc.push(a);
-      }
-      expect(acc).toEqual([5, 1, 2]);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", async function () {
-      const res = await pipe(
-        [1, 2, 3, 4, 5, 1, 2],
-        toAsync,
-        map((a) => a + 10),
-        filter((a) => a % 2 === 1),
-        dropUntil((a) => a > 13),
-        toArray,
-      );
-
-      expect(res).toEqual([11]);
-    });
-
-    it("should be able to handle an error when asynchronous", async function () {
-      await expect(
-        pipe(
-          toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
-          dropUntil((a) => {
-            if (a > 5) {
-              throw new Error("err");
-            }
-            return false;
-          }),
-          toArray,
-        ),
-      ).rejects.toThrow("err");
-    });
-
-    it("should be dropped elements concurrently", async function () {
-      const fn = jest.fn();
-      callFuncAfterTime(fn, 300);
-      const res = await pipe(
-        [1, 2, 3, 4, 5, 1, 2],
-        toAsync,
-        map((a) => delay(100, a + 10)),
-        filter((a) => a % 2 === 1),
-        dropUntil((a) => a > 13),
-        concurrent(3),
-        toArray,
-      );
-      expect(fn).toBeCalled();
-      expect(res).toEqual([11]);
-    }, 350);
-
-    it("should be controlled the order when concurrency", async function () {
-      const fn = jest.fn();
-      callFuncAfterTime(fn, 1000);
-
-      const res = await pipe(
-        toAsync(
-          (function* () {
-            yield delay(500, 1);
-            yield delay(400, 2);
-            yield delay(300, 3);
-            yield delay(200, 4);
-            yield delay(100, 5);
-            yield delay(500, 11);
-            yield delay(400, 12);
-            yield delay(300, 13);
-            yield delay(200, 8);
-            yield delay(100, 9);
-          })(),
-        ),
-        dropUntil((a) => a > 12),
-        concurrent(5),
-        toArray,
-      );
-      expect(fn).toBeCalled();
-      expect(res).toEqual([8, 9]);
-    }, 1050);
-
-    it("should be able to handle an error when working concurrent", async function () {
-      await expect(
-        pipe(
-          toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
-          dropUntil((a) => {
-            if (a > 5) {
-              throw new Error("err");
-            }
-            return false;
-          }),
-          concurrent(3),
-          toArray,
-        ),
-      ).rejects.toThrow("err");
-    });
-
-    it("should be able to handle an error when working concurrent - Promise.reject", async function () {
-      await expect(
-        pipe(
-          toAsync([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
-          dropUntil((a) => {
-            if (a > 5) {
-              return Promise.reject(new Error("err"));
-            }
-            return false;
-          }),
-          concurrent(3),
-          toArray,
-        ),
-      ).rejects.toThrow("err");
-    });
-
-    it("should be passed concurrent object when job works concurrently", async function () {
-      const mock = generatorMock();
-      const iter = dropUntil((a) => a, mock);
-      const concurrent = Concurrent.of(2) as any;
-
-      await iter.next(concurrent);
-      expect((mock as any).getConcurrent()).toEqual(concurrent);
-    });
-  });
-});
diff --git a/test/Lazy/pluck.spec.ts b/test/Lazy/pluck.spec.ts
new file mode 100644
index 00000000..90d40eee
--- /dev/null
+++ b/test/Lazy/pluck.spec.ts
@@ -0,0 +1,53 @@
+import { filter, pipe, toArray, toAsync } from "../../src";
+import pluck from "../../src/Lazy/pluck";
+
+describe("pluck", function () {
+  const given = [
+    { id: 1, age: 21 },
+    { id: 2, age: 22 },
+    { id: 3, age: 23 },
+    { id: 4, age: 24 },
+  ];
+
+  describe("sync", function () {
+    it("should return Iterable by plucking the same named property off all objects in the Iterable supplied", function () {
+      const acc = [];
+      for (const a of pluck("age", given)) {
+        acc.push(a);
+      }
+      expect(acc).toEqual([21, 22, 23, 24]);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const res = pipe(
+        given,
+        pluck("age"),
+        filter((a) => a > 21),
+        toArray,
+      );
+
+      expect(res).toEqual([22, 23, 24]);
+    });
+  });
+
+  describe("async", function () {
+    it("should return Iterable by plucking the same named property off all objects in the Iterable supplied", async function () {
+      const acc = [];
+      for await (const a of pluck("age", toAsync(given))) {
+        acc.push(a);
+      }
+      expect(acc).toEqual([21, 22, 23, 24]);
+    });
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const res = await pipe(
+        toAsync(given),
+        pluck("age"),
+        filter((a) => a > 21),
+        toArray,
+      );
+
+      expect(res).toEqual([22, 23, 24]);
+    });
+  });
+});
diff --git a/test/Lazy/split.spec.ts b/test/Lazy/split.spec.ts
deleted file mode 100644
index 710a599f..00000000
--- a/test/Lazy/split.spec.ts
+++ /dev/null
@@ -1,161 +0,0 @@
-import {
-  pipe,
-  split,
-  toAsync,
-  map,
-  toArray,
-  filter,
-  delay,
-  concurrent,
-} from "../../src";
-import { Concurrent } from "../../src/Lazy/concurrent";
-import { callFuncAfterTime, generatorMock } from "../utils";
-
-describe("split", function () {
-  describe("sync", function () {
-    it("should return an empty array", function () {
-      const iter = split("", "");
-      expect([...iter]).toEqual([]);
-    });
-
-    it("should be splited by empty string", function () {
-      const iter = split("", "abcdefg");
-      expect([...iter]).toEqual(["a", "b", "c", "d", "e", "f", "g"]);
-    });
-
-    it("should be splited by separator", function () {
-      const iter = split(",", "a,b,c,d,e,f,g");
-      expect([...iter]).toEqual(["a", "b", "c", "d", "e", "f", "g"]);
-    });
-
-    it("should be appended empty string if there is a separator at the end", function () {
-      const iter = split(",", "a,b,c,d,e,f,g,");
-      expect([...iter]).toEqual(["a", "b", "c", "d", "e", "f", "g", ""]);
-    });
-
-    it("should be splited by separator(unicode)", function () {
-      const iter = split(",", "👍,😀,🙇‍♂️,🤩,🎉");
-      expect([...iter]).toEqual(["👍", "😀", "🙇‍♂️", "🤩", "🎉"]);
-    });
-
-    it("should be able to be used as a curried function in the pipeline", function () {
-      const res = pipe(
-        "1,2,3,4,5,6,7,8,9,10",
-        split(","),
-        map((a) => Number(a)),
-        filter((a) => a % 2 === 0),
-        toArray,
-      );
-
-      expect(res).toEqual([2, 4, 6, 8, 10]);
-    });
-  });
-
-  describe("async", function () {
-    it("should return an empty array", async function () {
-      const acc = [];
-      for await (const a of split("", toAsync(""))) {
-        acc.push(a);
-      }
-      expect(acc).toEqual([]);
-    });
-
-    it("should be splited by empty string", async function () {
-      const acc = [];
-      for await (const a of split("", "abcdefg")) {
-        acc.push(a);
-      }
-      expect(acc).toEqual(["a", "b", "c", "d", "e", "f", "g"]);
-    });
-
-    it("should be splited by separator", async function () {
-      const acc = [];
-      for await (const a of split(",", "a,b,c,d,e,f,g")) {
-        acc.push(a);
-      }
-      expect(acc).toEqual(["a", "b", "c", "d", "e", "f", "g"]);
-    });
-
-    it("should be appended empty string if there is a separator at the end", async function () {
-      const acc = [];
-      for await (const a of split(",", "a,b,c,d,e,f,g,")) {
-        acc.push(a);
-      }
-      expect(acc).toEqual(["a", "b", "c", "d", "e", "f", "g", ""]);
-    });
-
-    it("should be splited by separator(unicode)", async function () {
-      const acc = [];
-      for await (const a of split(",", "👍,😀,🙇‍♂️,🤩,🎉")) {
-        acc.push(a);
-      }
-      expect(acc).toEqual(["👍", "😀", "🙇‍♂️", "🤩", "🎉"]);
-    });
-
-    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,5,6,7,8,9,10"),
-        split(","),
-        map((a) => Number(a)),
-        filter((a) => a % 2 === 0),
-        toArray,
-      );
-
-      expect(res).toEqual([2, 4, 6, 8, 10]);
-    });
-
-    it("should be controlled the order when concurrency", async function () {
-      const fn = jest.fn();
-      callFuncAfterTime(fn, 1000);
-
-      const res = await pipe(
-        toAsync(
-          (function* () {
-            yield delay(500, "1");
-            yield delay(400, ",");
-            yield delay(300, "2");
-            yield delay(200, ",");
-            yield delay(100, "3");
-            yield delay(500, ",");
-            yield delay(400, "4");
-            yield delay(300, ",");
-            yield delay(200, "5");
-          })(),
-        ),
-        split(","),
-        concurrent(5),
-        toArray,
-      );
-
-      expect(fn).toBeCalled();
-      expect(res).toEqual(["1", "2", "3", "4", "5"]);
-    }, 1050);
-
-    it("should be consumed concurrently", async function () {
-      const fn = jest.fn();
-      callFuncAfterTime(fn, 1000);
-
-      const res = await pipe(
-        toAsync("1,2,3,4,5,6,7,8,9,10"),
-        split(","),
-        map((a) => delay(500, a)),
-        map((a) => Number(a)),
-        filter((a) => a % 2 === 0),
-        concurrent(5),
-        toArray,
-      );
-
-      expect(fn).toBeCalled();
-      expect(res).toEqual([2, 4, 6, 8, 10]);
-    }, 1050);
-  });
-
-  it("should be passed concurrent object when job works concurrently", async function () {
-    const mock = generatorMock();
-    const iter = split(",", mock as AsyncIterable<string>);
-    const concurrent = Concurrent.of(2) as any;
-
-    await iter.next(concurrent);
-    expect((mock as any).getConcurrent()).toEqual(concurrent);
-  });
-});
diff --git a/type-check/Lazy/dropUntil.test.ts b/type-check/Lazy/dropUntil.test.ts
deleted file mode 100644
index 435e96a7..00000000
--- a/type-check/Lazy/dropUntil.test.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import * as Test from "../../src/types/Test";
-import { toAsync, dropUntil, pipe } from "../../src";
-
-const { checks, check } = Test;
-
-const res1 = dropUntil((a) => a, []);
-const res2 = dropUntil((a) => a > 1, [1, 2, 3, 4]);
-const res3 = dropUntil((a) => a > 1, toAsync([1, 2, 3, 4]));
-const res4 = pipe(
-  [1, 2, 3, 4],
-  dropUntil((a) => a > 1),
-);
-const res5 = pipe(
-  toAsync([1, 2, 3, 4]),
-  dropUntil((a) => a > 1),
-);
-
-checks([
-  check<typeof res1, IterableIterator<never>, Test.Pass>(),
-  check<typeof res2, IterableIterator<number>, Test.Pass>(),
-  check<typeof res3, AsyncIterableIterator<number>, Test.Pass>(),
-  check<typeof res4, IterableIterator<number>, Test.Pass>(),
-  check<typeof res5, AsyncIterableIterator<number>, Test.Pass>(),
-]);
diff --git a/type-check/Lazy/pluck.test.ts b/type-check/Lazy/pluck.test.ts
new file mode 100644
index 00000000..9bc3082a
--- /dev/null
+++ b/type-check/Lazy/pluck.test.ts
@@ -0,0 +1,24 @@
+import * as Test from "../../src/types/Test";
+import { pluck, toAsync, pipe } from "../../src";
+
+const { checks, check } = Test;
+const given = [
+  { id: 1, age: 21 },
+  { id: 2, age: 22 },
+  { id: 3, age: 23 },
+  { id: 4, age: 24 },
+];
+
+const res1 = pluck("age", given);
+const res2 = pluck("age", toAsync(given));
+
+const res3 = pipe(given, pluck("age"));
+const res5 = pipe(toAsync(given), pluck("age"));
+
+checks([
+  check<typeof res1, IterableIterator<number>, Test.Pass>(),
+  check<typeof res2, AsyncIterableIterator<number>, Test.Pass>(),
+
+  check<typeof res3, IterableIterator<number>, Test.Pass>(),
+  check<typeof res5, AsyncIterableIterator<number>, Test.Pass>(),
+]);
diff --git a/type-check/Lazy/split.test.ts b/type-check/Lazy/split.test.ts
deleted file mode 100644
index 3a011b1e..00000000
--- a/type-check/Lazy/split.test.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as Test from "../../src/types/Test";
-import { split, toAsync, pipe } from "../../src";
-
-const { checks, check } = Test;
-
-const res1 = split(",", "1,2,3");
-const res2 = split(",", toAsync("1,2,3"));
-
-const res3 = pipe("1,2,3,4", split(","));
-const res4 = pipe(toAsync("1,2,3,4"), split(","));
-
-checks([
-  check<typeof res1, IterableIterator<string>, Test.Pass>(),
-  check<typeof res2, AsyncIterableIterator<string>, Test.Pass>(),
-  check<typeof res3, IterableIterator<string>, Test.Pass>(),
-  check<typeof res4, AsyncIterableIterator<string>, Test.Pass>(),
-]);