-
Notifications
You must be signed in to change notification settings - Fork 0
/
fold.ts
153 lines (151 loc) · 4.78 KB
/
fold.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
/**
* Apply `reducer` function of two arguments cumulatively to the elements of
* an async iterable `source`, from left to right, so as to reduce the async
* iterable to a single value.
*
* ``` typescript
* import { reduce } from "./fold.ts";
*
* async function* oneToFive() { yield 1; yield 2; yield 3; yield 4; yield 5; }
* const reducedValue = await reduce(
* (x, y) => { console.log(`${x} + ${y} = ${x + y}`); return x + y; },
* oneToFive(),
* 0,
* );
* console.log("reduced value:", reducedValue);
* ```
*
* The above example will print the following:
*
* ~~~
* 0 + 1 = 1
* 1 + 2 = 3
* 3 + 3 = 6
* 6 + 4 = 10
* 10 + 5 = 15
* reduced value: 15
* ~~~
*
* If the async iterable `source` is empty, the `reducer` function is not
* called and `initialValue` is returned.
*
* ``` typescript
* import { reduce } from "./fold.ts";
*
* const reducedValue = await reduce(
* (x, y) => { console.log(`${x} + ${y} = ${x * y}`); return x * y; },
* [],
* 1,
* );
* console.log("reduced value:", reducedValue);
* ```
*
* The above example will print the following:
*
* ~~~
* reduced value: 1
* ~~~
*
* @template E The type of the elements in the `source`.
* @template V The type of the `initialValue` and the reduced values.
* @param reducer A binary operation that accepts the previous reduction result
* and the next element in the async iterable, and returns
* the next reduction result.
* @param source The async iterable to reduce over the elements into a single
* value. It should be finite, and can be empty.
* @param initialValue The initial value of the accumulator. It's usually
* the identity element for the `reducer` function,
* e.g., `0` for `(x, y) => x + y`, or `1` for
* `(x, y) => x * y`. If `source` is empty, this value
* is returned.
* @returns The final result of the reduction.
*/
export function reduce<E, V>(
reducer: (reducedValue: V, element: E) => V,
source: Iterable<E> | AsyncIterable<E>,
initialValue: V,
): Promise<V>;
/**
* Apply `reducer` function of two arguments cumulatively to the elements of
* an async iterable `source`, from left to right, so as to reduce the async
* iterable to a single value.
*
* ``` typescript
* import { reduce } from "./fold.ts";
*
* async function* oneToFive() { yield 1; yield 2; yield 3; yield 4; yield 5; }
* const reducedValue = await reduce<number, number>(
* (x, y) => { console.log(`${x} + ${y} = ${x + y}`); return x + y; },
* oneToFive(),
* );
* console.log("reduced value:", reducedValue);
* ```
*
* The above example will print the following:
*
* ~~~
* 1 + 2 = 3
* 3 + 3 = 6
* 6 + 4 = 10
* 10 + 5 = 15
* reduced value: 15
* ~~~
*
* If the async iterable `source` is empty, it throws an `TypeError`.
*
* ``` typescript
* import { reduce } from "./fold.ts";
*
* const reducedValue = await reduce<number, number>(
* (x, y) => { console.log(`${x} + ${y} = ${x * y}`); return x * y; },
* [],
* );
* console.log("reduced value:", reducedValue);
* ```
*
* The above example will print the following:
*
* ~~~
* Uncaught TypeError: reduce without initialValue requires at least one element
* at reduce ...
* at async ...
* ~~~
*
* @template E The type of the elements in the `source`.
* @template V The type of the reduced values.
* @param reducer A binary operation that accepts the previous reduction result
* and the next element in the async iterable, and returns
* the next reduction result.
* @param source The async iterable to reduce over the elements into a single
* value. It should be finite, and must not be empty. If you
* want to reduce a potentially empty async iterable,
* call the `reduce` function with an `initialValue`.
* @returns The final result of the reduction.
* @throws {TypeError} when the async iterable `source` is empty.
*/
export function reduce<E, V>(
reducer: (reducedValue: E | V, element: E) => V,
source: Iterable<E> | AsyncIterable<E>,
): Promise<E | V>;
export async function reduce<E, V>(
reducer: (reducedValue: E | V, element: E) => V,
source: Iterable<E> | AsyncIterable<E>,
initialValue?: V,
) {
let state:
| { initialized: false }
| { initialized: true; value: E | V } = arguments.length < 3
? { initialized: false }
: { initialized: true, value: initialValue as V };
for await (const element of source) {
if (state.initialized) {
state.value = reducer(state.value, element);
} else {
state = { initialized: true, value: element };
}
}
if (state.initialized) return state.value;
throw new TypeError(
"reduce without initialValue requires at least one element",
);
}