/
reduce.ts
106 lines (98 loc) · 3.2 KB
/
reduce.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
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
/*-----------------------------------------------------------------------------
| Copyright (c) 2014-2017, PhosphorJS Contributors
|
| Distributed under the terms of the BSD 3-Clause License.
|
| The full license is in the file LICENSE, distributed with this software.
|----------------------------------------------------------------------------*/
/**
* Summarize all values in an iterable using a reducer function.
*
* @param object - The iterable object of interest.
*
* @param fn - The reducer function to invoke for each value.
*
* @param initial - The initial value to start accumulation.
*
* @returns The final accumulated value.
*
* #### Notes
* The `reduce` function follows the conventions of `Array#reduce`.
*
* If the iterator is empty, an initial value is required. That value
* will be used as the return value. If no initial value is provided,
* an error will be thrown.
*
* If the iterator contains a single item and no initial value is
* provided, the single item is used as the return value.
*
* Otherwise, the reducer is invoked for each element in the iterable.
* If an initial value is not provided, the first element will be used
* as the initial accumulated value.
*
* #### Complexity
* Linear.
*
* #### Example
* ```typescript
* import { reduce } from '@lumino/algorithm';
*
* let data = [1, 2, 3, 4, 5];
*
* let sum = reduce(data, (a, value) => a + value); // 15
* ```
*/
export function reduce<T>(
object: Iterable<T>,
fn: (accumulator: T, value: T, index: number) => T
): T;
export function reduce<T, U>(
object: Iterable<T>,
fn: (accumulator: U, value: T, index: number) => U,
initial: U
): U;
export function reduce<T>(
object: Iterable<T>,
fn: (accumulator: any, value: T, index: number) => any,
initial?: unknown
): any {
// Setup the iterator and fetch the first value.
const it = object[Symbol.iterator]();
let index = 0;
let first = it.next();
// An empty iterator and no initial value is an error.
if (first.done && initial === undefined) {
throw new TypeError('Reduce of empty iterable with no initial value.');
}
// If the iterator is empty, return the initial value.
if (first.done) {
return initial;
}
// If the iterator has a single item and no initial value, the
// reducer is not invoked and the first item is the return value.
let second = it.next();
if (second.done && initial === undefined) {
return first.value;
}
// If iterator has a single item and an initial value is provided,
// the reducer is invoked and that result is the return value.
if (second.done) {
return fn(initial, first.value, index++);
}
// Setup the initial accumlated value.
let accumulator: any;
if (initial === undefined) {
accumulator = fn(first.value, second.value, index++);
} else {
accumulator = fn(fn(initial, first.value, index++), second.value, index++);
}
// Iterate the rest of the values, updating the accumulator.
let next: IteratorResult<T>;
while (!(next = it.next()).done) {
accumulator = fn(accumulator, next.value, index++);
}
// Return the final accumulated value.
return accumulator;
}