Repository: marpple/FxTS (ts)

Issue:
Add `size` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

## 💻 Use Cases

```ts
size(range(10, 16)); // 5
await size(toAsync(range(10, 16))); // 5
```

Pull request description:
feat: add `size` function
Fixes #75


Patch:
diff --git a/src/index.ts b/src/index.ts
index ae41e4e4..ce6df37a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -35,6 +35,7 @@ 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";
 import tap from "./tap";
@@ -80,6 +81,7 @@ export {
   pipe1,
   reduce,
   reverse,
+  size,
   some,
   sort,
   tap,
diff --git a/src/size.ts b/src/size.ts
new file mode 100644
index 00000000..15ee83d7
--- /dev/null
+++ b/src/size.ts
@@ -0,0 +1,45 @@
+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;


Test patch:
diff --git a/test/size.spec.ts b/test/size.spec.ts
new file mode 100644
index 00000000..89a78a1a
--- /dev/null
+++ b/test/size.spec.ts
@@ -0,0 +1,24 @@
+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/type-check/size.test.ts b/type-check/size.test.ts
new file mode 100644
index 00000000..609476f5
--- /dev/null
+++ b/type-check/size.test.ts
@@ -0,0 +1,20 @@
+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>(),
+]);