Repository: marpple/FxTS (ts)

Issue:
Add `consume` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

Consumes the given number of `Iterable/AsyncIterable`. If the number is empty, all is consumed.

## 💻 Use Cases

<!-- What do you want to use this for? -->

```ts
const iterator = (function *(){ 
  yield 1; 
  yield 2; 
  yield 3;  
})();
consume(iterator, 2);
iterator.next(); // {value:3, done:false}

// with pipe
pipe(
  range(10),
  peek(updateApi),
  concurrent(5),
  consume,
);

```


Pull request:
feat: add `consume` function
Fixes #66 


Patch:
diff --git a/src/consume.ts b/src/consume.ts
new file mode 100644
index 00000000..b043885a
--- /dev/null
+++ b/src/consume.ts
@@ -0,0 +1,68 @@
+import ReturnValueType from "./types/ReturnValueType";
+import { isAsyncIterable, isIterable } from "./_internal/utils";
+import range from "./Lazy/range";
+
+function sync<T>(iterable: Iterable<T>, n: number) {
+  const iterator = iterable[Symbol.iterator]();
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  for (const _ of range(0, n)) {
+    if (iterator.next().done) {
+      return;
+    }
+  }
+}
+
+async function async<T>(iterable: AsyncIterable<T>, n: number) {
+  const iterator = iterable[Symbol.asyncIterator]();
+  // eslint-disable-next-line @typescript-eslint/no-unused-vars
+  for await (const _ of range(0, n)) {
+    if ((await iterator.next()).done) {
+      return;
+    }
+  }
+}
+
+/**
+ * Consumes the given number of Iterable/AsyncIterable. If the number is empty, all is consumed.
+ *
+ * @example
+ * ```ts
+ * const iterator = (function *(){
+ *   yield 1;
+ *   yield 2;
+ *   yield 3;
+ * })();
+ * consume(iterator, 2);
+ * iterator.next(); // {value:3, done:false}
+ * iterator.next(); // {value:undefined, done:true}
+ *
+ * // with pipe
+ * pipe(
+ *   range(10),
+ *   peek(updateApi),
+ *   concurrent(5),
+ *   consume,
+ * );
+ * ```
+ */
+function consume<
+  A extends Iterable<unknown> | AsyncIterable<unknown>,
+  B extends number,
+>(iterator: A, n?: B): ReturnValueType<A, void>;
+
+function consume<A extends Iterable<unknown> | AsyncIterable<unknown>>(
+  iterable: A,
+  n = Infinity,
+) {
+  if (isIterable(iterable)) {
+    return sync(iterable, n);
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return async(iterable, n);
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default consume;
diff --git a/src/index.ts b/src/index.ts
index 39a81097..905db6e5 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,3 +1,4 @@
+import consume from "./consume";
 import countBy from "./countBy";
 import curry from "./curry";
 import delay from "./delay";
@@ -16,6 +17,7 @@ import isObject from "./isObject";
 import isString from "./isString";
 import join from "./join";
 import last from "./last";
+import noop from "./noop";
 import not from "./not";
 import nth from "./nth";
 import omit from "./omit";
@@ -32,6 +34,7 @@ import tap from "./tap";
 import toArray from "./toArray";
 
 export {
+  consume,
   countBy,
   curry,
   delay,
@@ -52,6 +55,7 @@ export {
   isString,
   join,
   last,
+  noop,
   not,
   nth,
   omit,
diff --git a/src/noop.ts b/src/noop.ts
new file mode 100644
index 00000000..1753010e
--- /dev/null
+++ b/src/noop.ts
@@ -0,0 +1,12 @@
+/**
+ * Returns `undefined`
+ *
+ * @example
+ * ```ts
+ * noop() // undefined
+ * ```
+ */
+// eslint-disable-next-line @typescript-eslint/no-empty-function
+function noop(): void {}
+
+export default noop;


Test patch:
diff --git a/test/consume.spec.ts b/test/consume.spec.ts
new file mode 100644
index 00000000..fee64ba0
--- /dev/null
+++ b/test/consume.spec.ts
@@ -0,0 +1,89 @@
+import { consume, peek, pipe, toAsync } from "../src/index";
+
+describe("consume", function () {
+  describe("sync", function () {
+    const testParameters = [
+      {
+        input: [1, 2, 3, 4, 5][Symbol.iterator](),
+        n: 2,
+        expected: 3,
+      },
+      {
+        input: [1, 2, 3, 4, 5][Symbol.iterator](),
+        n: undefined,
+        expected: undefined,
+      },
+      {
+        input: [1, 2, 3, 4, 5][Symbol.iterator](),
+        n: Infinity,
+        expected: undefined,
+      },
+    ];
+
+    it.each(testParameters)(
+      "should be consumed the given number",
+      ({ input, n, expected }) => {
+        consume(input, n);
+        expect(input.next().value).toEqual(expected);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", function () {
+      const iter = [1, 2, 3, 4][Symbol.iterator]();
+      let res = 0;
+      pipe(
+        iter,
+        peek((a) => {
+          res += a;
+        }),
+        consume,
+      );
+
+      expect(res).toEqual(10);
+      expect(iter.next().done).toEqual(true);
+    });
+  });
+
+  describe("async", function () {
+    const testParameters = [
+      {
+        input: toAsync([1, 2, 3, 4, 5])[Symbol.asyncIterator](),
+        n: 2,
+        expected: 3,
+      },
+      {
+        input: toAsync([1, 2, 3, 4, 5])[Symbol.asyncIterator](),
+        n: undefined,
+        expected: undefined,
+      },
+      {
+        input: toAsync([1, 2, 3, 4, 5])[Symbol.asyncIterator](),
+        n: Infinity,
+        expected: undefined,
+      },
+    ];
+
+    it.each(testParameters)(
+      "should be consumed the given number",
+      async ({ input, n, expected }) => {
+        await consume(input, n);
+        expect((await input.next()).value).toEqual(expected);
+      },
+    );
+
+    it("should be able to be used as a curried function in the pipeline", async function () {
+      const iter = toAsync([1, 2, 3, 4])[Symbol.asyncIterator]();
+      let res = 0;
+      await pipe(
+        iter,
+        peek((a) => {
+          res += a;
+        }),
+        consume,
+      );
+
+      expect(res).toEqual(10);
+      expect((await iter.next()).done).toEqual(true);
+    });
+  });
+});
diff --git a/test/noop.spec.ts b/test/noop.spec.ts
new file mode 100644
index 00000000..f293be99
--- /dev/null
+++ b/test/noop.spec.ts
@@ -0,0 +1,7 @@
+import { noop } from "../src/index";
+
+describe("noop", function () {
+  it("should return `undefined`", function () {
+    expect(noop()).toEqual(undefined);
+  });
+});
diff --git a/type-check/consume.test.ts b/type-check/consume.test.ts
new file mode 100644
index 00000000..29164bbc
--- /dev/null
+++ b/type-check/consume.test.ts
@@ -0,0 +1,16 @@
+import * as Test from "../src/types/Test";
+import { consume, pipe, toAsync } from "../src";
+
+const { checks, check } = Test;
+
+const res1 = consume([1, 2, 3]);
+const res2 = pipe([1, 2, 3], consume);
+const res3 = pipe([1, 2, 3], (res) => consume(res, 2));
+const res4 = pipe([1, 2, 3], toAsync, consume);
+
+checks([
+  check<typeof res1, void, Test.Pass>(),
+  check<typeof res2, void, Test.Pass>(),
+  check<typeof res3, void, Test.Pass>(),
+  check<typeof res4, Promise<void>, Test.Pass>(),
+]);
diff --git a/type-check/noop.test.ts b/type-check/noop.test.ts
new file mode 100644
index 00000000..a9ac8b3b
--- /dev/null
+++ b/type-check/noop.test.ts
@@ -0,0 +1,8 @@
+import * as Test from "../src/types/Test";
+import { noop } from "../src";
+
+const { checks, check } = Test;
+
+const res = noop();
+
+checks([check<typeof res, void, Test.Pass>()]);