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