1 | // Copyright (c) Jupyter Development Team.
|
2 | // Distributed under the terms of the Modified BSD License.
|
3 | /*-----------------------------------------------------------------------------
|
4 | | Copyright (c) 2014-2017, PhosphorJS Contributors
|
5 | |
|
6 | | Distributed under the terms of the BSD 3-Clause License.
|
7 | |
|
8 | | The full license is in the file LICENSE, distributed with this software.
|
9 | |----------------------------------------------------------------------------*/
|
10 |
|
11 | /**
|
12 | * Summarize all values in an iterable using a reducer function.
|
13 | *
|
14 | * @param object - The iterable object of interest.
|
15 | *
|
16 | * @param fn - The reducer function to invoke for each value.
|
17 | *
|
18 | * @param initial - The initial value to start accumulation.
|
19 | *
|
20 | * @returns The final accumulated value.
|
21 | *
|
22 | * #### Notes
|
23 | * The `reduce` function follows the conventions of `Array#reduce`.
|
24 | *
|
25 | * If the iterator is empty, an initial value is required. That value
|
26 | * will be used as the return value. If no initial value is provided,
|
27 | * an error will be thrown.
|
28 | *
|
29 | * If the iterator contains a single item and no initial value is
|
30 | * provided, the single item is used as the return value.
|
31 | *
|
32 | * Otherwise, the reducer is invoked for each element in the iterable.
|
33 | * If an initial value is not provided, the first element will be used
|
34 | * as the initial accumulated value.
|
35 | *
|
36 | * #### Complexity
|
37 | * Linear.
|
38 | *
|
39 | * #### Example
|
40 | * ```typescript
|
41 | * import { reduce } from '@lumino/algorithm';
|
42 | *
|
43 | * let data = [1, 2, 3, 4, 5];
|
44 | *
|
45 | * let sum = reduce(data, (a, value) => a + value); // 15
|
46 | * ```
|
47 | */
|
48 | export function reduce<T>(
|
49 | object: Iterable<T>,
|
50 | fn: (accumulator: T, value: T, index: number) => T
|
51 | ): T;
|
52 | export function reduce<T, U>(
|
53 | object: Iterable<T>,
|
54 | fn: (accumulator: U, value: T, index: number) => U,
|
55 | initial: U
|
56 | ): U;
|
57 | export function reduce<T>(
|
58 | object: Iterable<T>,
|
59 | fn: (accumulator: any, value: T, index: number) => any,
|
60 | initial?: unknown
|
61 | ): any {
|
62 | // Setup the iterator and fetch the first value.
|
63 | const it = object[Symbol.iterator]();
|
64 | let index = 0;
|
65 | let first = it.next();
|
66 |
|
67 | // An empty iterator and no initial value is an error.
|
68 | if (first.done && initial === undefined) {
|
69 | throw new TypeError('Reduce of empty iterable with no initial value.');
|
70 | }
|
71 |
|
72 | // If the iterator is empty, return the initial value.
|
73 | if (first.done) {
|
74 | return initial;
|
75 | }
|
76 |
|
77 | // If the iterator has a single item and no initial value, the
|
78 | // reducer is not invoked and the first item is the return value.
|
79 | let second = it.next();
|
80 | if (second.done && initial === undefined) {
|
81 | return first.value;
|
82 | }
|
83 |
|
84 | // If iterator has a single item and an initial value is provided,
|
85 | // the reducer is invoked and that result is the return value.
|
86 | if (second.done) {
|
87 | return fn(initial, first.value, index++);
|
88 | }
|
89 |
|
90 | // Setup the initial accumlated value.
|
91 | let accumulator: any;
|
92 | if (initial === undefined) {
|
93 | accumulator = fn(first.value, second.value, index++);
|
94 | } else {
|
95 | accumulator = fn(fn(initial, first.value, index++), second.value, index++);
|
96 | }
|
97 |
|
98 | // Iterate the rest of the values, updating the accumulator.
|
99 | let next: IteratorResult<T>;
|
100 | while (!(next = it.next()).done) {
|
101 | accumulator = fn(accumulator, next.value, index++);
|
102 | }
|
103 |
|
104 | // Return the final accumulated value.
|
105 | return accumulator;
|
106 | }
|