Skip to content

Commit 469e7df

Browse files
authored
feat: change reduce behavior, when empty iterable (#236)
1 parent 6d42931 commit 469e7df

File tree

6 files changed

+44
-19
lines changed

6 files changed

+44
-19
lines changed

src/every.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,12 @@ function every<
5858
return pipe(
5959
map(f, iterable),
6060
takeUntil(not),
61-
reduce((a, b) => a && b),
61+
(acc) =>
62+
reduce(
63+
(a: boolean, b: boolean) => a && b,
64+
true,
65+
acc as Iterable<boolean>,
66+
),
6267
(a) => a ?? true,
6368
Boolean,
6469
);
@@ -68,7 +73,12 @@ function every<
6873
return pipe(
6974
map(f, iterable),
7075
takeUntil(not),
71-
reduce((a, b) => a && b),
76+
(acc) =>
77+
reduce(
78+
(a: boolean, b: boolean) => a && b,
79+
true,
80+
acc as AsyncIterable<boolean>,
81+
),
7282
(a) => a ?? true,
7383
Boolean,
7484
);

src/join.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ type ReturnJoinType<T extends Iterable<unknown> | AsyncIterable<unknown>> =
1010
: never;
1111

1212
function sync<A>(sep: string, iterable: Iterable<A>) {
13-
const res = reduce((a: string, b) => `${a}${sep}${b}`, iterable);
13+
const res = reduce(
14+
(a: string, b) => (a == "" ? `${b}` : `${a}${sep}${b}`),
15+
"",
16+
iterable,
17+
);
1418
if (res == null) {
1519
return "";
1620
}
@@ -19,7 +23,11 @@ function sync<A>(sep: string, iterable: Iterable<A>) {
1923
}
2024

2125
function async<A>(sep: string, iterable: AsyncIterable<A>) {
22-
return reduce((a: string, b) => `${a}${sep}${b}`, iterable).then((res) => {
26+
return reduce(
27+
(a: string, b) => (a == "" ? `${b}` : `${a}${sep}${b}`),
28+
"",
29+
iterable,
30+
).then((res) => {
2331
if (res == null) {
2432
return "";
2533
}

src/reduce.ts

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ async function async<A, B>(
7272
* {@link https://fxts.dev/docs/map | map}, {@link https://fxts.dev/docs/filter | filter}
7373
*/
7474

75-
function reduce<A extends readonly []>(f: Arrow, iterable: A): undefined;
76-
7775
function reduce<A extends readonly [], B>(f: Arrow, seed: B, iterable: A): B;
7876

7977
function reduce<A>(f: (a: A, b: A) => A, iterable: Iterable<A>): A;
@@ -113,11 +111,7 @@ function reduce<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
113111
f: (a: B, b: IterableInfer<A>) => B,
114112
seed?: B | Iterable<IterableInfer<A>> | AsyncIterable<IterableInfer<A>>,
115113
iterable?: Iterable<IterableInfer<A>> | AsyncIterable<IterableInfer<A>>,
116-
):
117-
| B
118-
| undefined
119-
| Promise<B | undefined>
120-
| ((iterable: A) => ReturnValueType<A, B>) {
114+
): B | Promise<B> | ((iterable: A) => ReturnValueType<A, B>) {
121115
if (iterable === undefined) {
122116
if (seed === undefined) {
123117
return (iterable: A) =>
@@ -128,7 +122,7 @@ function reduce<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
128122
const iterator = seed[Symbol.iterator]();
129123
const { done, value } = iterator.next();
130124
if (done) {
131-
return undefined;
125+
throw new TypeError("'reduce' of empty iterable with no initial value");
132126
}
133127
return sync(f, value as B, {
134128
[Symbol.iterator]() {
@@ -141,8 +135,11 @@ function reduce<A extends Iterable<unknown> | AsyncIterable<unknown>, B>(
141135
const iterator = seed[Symbol.asyncIterator]();
142136
return iterator.next().then(({ done, value }) => {
143137
if (done) {
144-
return undefined;
138+
throw new TypeError(
139+
"'reduce' of empty iterable with no initial value",
140+
);
145141
}
142+
146143
return async(f, value as Promise<B>, {
147144
[Symbol.asyncIterator]() {
148145
return iterator;

src/some.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,12 @@ function some<
7878
return pipe(
7979
map(f, iterable),
8080
takeUntil(identity),
81-
reduce((a, b) => a || b),
81+
(acc) =>
82+
reduce(
83+
(a: boolean, b: boolean) => a || b,
84+
false,
85+
acc as Iterable<boolean>,
86+
),
8287
Boolean,
8388
);
8489
}
@@ -87,7 +92,12 @@ function some<
8792
return pipe(
8893
map(f, iterable),
8994
takeUntil(identity),
90-
reduce((a, b) => a || b),
95+
(acc) =>
96+
reduce(
97+
(a: boolean, b: boolean) => a || b,
98+
false,
99+
acc as AsyncIterable<boolean>,
100+
),
91101
Boolean,
92102
);
93103
}

test/reduce.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ describe("reduce", function () {
99
expect(reduce((a, b) => a + b, "seed", [])).toEqual("seed");
1010
});
1111

12-
it("should return 'undefined' when the given `iterable` is an empty array and initial value is absent", function () {
13-
expect(reduce((a, b) => a + b, [])).toBeUndefined();
12+
it("should be occured error when the given `iterable` is an empty array and initial value is absent", function () {
13+
expect(() => reduce((a) => a, [])).toThrow();
1414
});
1515

1616
it("should work given it is initial value", function () {

type-check/reduce.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as Test from "../src/types/Test";
33

44
const { checks, check } = Test;
55

6-
const res1 = reduce((a, b) => a + b, []);
6+
const res1 = reduce((a) => a, []);
77
const res2 = reduce((a, b) => a + b, "seed", []);
88
const res3 = reduce((a, b) => a + b, 0, [1, 2, 3]);
99
const res4 = reduce((a, b) => a + b, [1, 2, 3]);
@@ -52,7 +52,7 @@ const res16 = pipe(
5252
);
5353

5454
checks([
55-
check<typeof res1, undefined, Test.Pass>(),
55+
check<typeof res1, never, Test.Pass>(),
5656
check<typeof res2, "seed", Test.Pass>(),
5757
check<typeof res3, number, Test.Pass>(),
5858
check<typeof res4, number, Test.Pass>(),

0 commit comments

Comments
 (0)