Repository: marpple/FxTS (ts)

Issue:
Add `sum` function
# Suggestion

<!--
  Please fill in each section completely. Thank you!
-->

## ⭐ Suggestion

<!-- What would you like to be added? -->

## 💻 Use Cases
```ts
sum([1,2,3,4,5]); // 15
sum(['a', 'b', 'c']); // 'abc'

await sum(toAsync([1,2,3,4,5])); // 15
await sum(toAsync(['a', 'b', 'c'])); // 'abc'
```

Pull request description:
feat: add `sum` function
Fixes #76 


Patch:
diff --git a/src/index.ts b/src/index.ts
index ae41e4e4..9fc0d51a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -35,8 +35,10 @@ 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 sum from "./sum";
 import tap from "./tap";
 import toArray from "./toArray";
 
@@ -80,8 +82,10 @@ export {
   pipe1,
   reduce,
   reverse,
+  size,
   some,
   sort,
+  sum,
   tap,
   toArray,
 };
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;
diff --git a/src/sum.ts b/src/sum.ts
new file mode 100644
index 00000000..534bf0c2
--- /dev/null
+++ b/src/sum.ts
@@ -0,0 +1,36 @@
+import add from "./add";
+import reduce from "./reduce";
+import IterableInfer from "./types/IterableInfer";
+import ReturnValueType from "./types/ReturnValueType";
+import { isAsyncIterable, isIterable } from "./_internal/utils";
+
+type ReturnSumType<
+  A extends Iterable<number | string> | AsyncIterable<number | string>,
+> = ReturnValueType<A, IterableInfer<A>>;
+
+/**
+ * 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 | string> | AsyncIterable<number | string>,
+>(iterable: A): ReturnSumType<A> {
+  if (isIterable(iterable)) {
+    return reduce(add, iterable) as ReturnSumType<A>;
+  }
+
+  if (isAsyncIterable(iterable)) {
+    return reduce(add, iterable) as ReturnSumType<A>;
+  }
+
+  throw new TypeError("'iterable' must be type of Iterable or AsyncIterable");
+}
+
+export default sum;


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/test/sum.spec.ts b/test/sum.spec.ts
new file mode 100644
index 00000000..0ff1d911
--- /dev/null
+++ b/test/sum.spec.ts
@@ -0,0 +1,35 @@
+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/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>(),
+]);
diff --git a/type-check/sum.test.ts b/type-check/sum.test.ts
new file mode 100644
index 00000000..ad4901ef
--- /dev/null
+++ b/type-check/sum.test.ts
@@ -0,0 +1,22 @@
+import * as Test from "../src/types/Test";
+import { sum, pipe, toAsync } from "../src";
+
+const { checks, check } = Test;
+
+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>(),
+]);