UNPKG

3.28 kBPlain TextView Raw
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 */
48export function reduce<T>(
49 object: Iterable<T>,
50 fn: (accumulator: T, value: T, index: number) => T
51): T;
52export function reduce<T, U>(
53 object: Iterable<T>,
54 fn: (accumulator: U, value: T, index: number) => U,
55 initial: U
56): U;
57export 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}