@hpcc-js/dataflow 9.6.7 → 9.6.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,530 +1,530 @@
1
- # @hpcc-js/dataflow
2
-
3
- _A small functional library for processing "data flows" in JavaScript ([more examples on ObservableHQ](https://observablehq.com/@gordonsmith/hpcc-js-dataflow)). Highlights:_
4
-
5
- * **Lazy Evaluation** - Implemented using modern JavaScript generators and iterators
6
- * **Memory Efficient** - Data "streams" from one "activity" to the next
7
- * **Functional** - Pure functional implementation.
8
- * **Fully Typed** - Written in typescript and supports typed chaining of functional activities
9
- * **UMD/ES6 Bundles** - Works in NodeJS / Browser and includes ES6 modules to ensure you only include what you use (when bundling with RollupJS / Webpack etc.)
10
-
11
- ## Motivation
12
-
13
- The underlying motivation for this library is to simplify the processing of data in an efficient way. The analogy we use is one of a "data" pipe, which consists of:
14
- * Activities: Functional components that modify data as it flows through the pipe.
15
- * Sensors: Functional components that observe the data as it passes through the pipe.
16
-
17
- Some other properties of pipes are:
18
- * Can be defined, before being used.
19
- * A complex pipe is just another "Activity" and as such can be re-used inside other pipes.
20
- * Encourages the user to only iterate through the source data ONCE allowing for less memory use and better overall performance.
21
-
22
- ## Terminology
23
-
24
- * **Activity** - A functional unit of work that is primary used to alter the data (`map`, `filter`, `sort`, ...).
25
- * **Sensor** - A function which "observes" the data without modifying it (`min`, `max`, `quartile`, ...).
26
- * **IterableActivity** - An "Activity" which produces an "Iterable" output (`map`, `filter`, `sort`, ...).
27
- * **ScalarActivity** - An "Activity" which produces a single value (`min`, `max`, `reduce`...).
28
- * **Process** or **Pipeline** - A series of "Activities" chained together, so that "data" "flows" through the process / pipeline.
29
-
30
- ## Quick Example
31
- _Simple example of data flowing through a `pipe` of activities: _`filter`->`map`->`filter`->`first`_
32
-
33
- ```javascript
34
- import { count, filter, first, generate, map, max, pipe, sensor } from "@hpcc-js/dataflow";
35
-
36
- const c1 = count();
37
- const c2 = count();
38
- const c3 = count();
39
- const m1 = max(row => row.value);
40
-
41
- const p1 = pipe(
42
- sensor(c1), // Keep running count of input
43
- filter(n => n <= 0.5), // Filter out numbers > 0.5
44
- sensor(c2), // Keep running count of filtered rows
45
- map((n, idx) => // Convert to JSON Object
46
- ({ index: idx, value: n })),
47
- filter(row => row.index % 2 === 0), // Filter even row indecies
48
- sensor(c3), // Keep running count of final rows
49
- sensor(m1), // Track largest value
50
- first(3) // Take first 3 rows
51
- );
52
-
53
- console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}`);
54
- // [1] => Counts: undefined, undefined, undefined
55
-
56
- const outIterable = p1(generate(Math.random, 1000));
57
- console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}`);
58
- // [2] => Counts: undefined, undefined, undefined
59
-
60
- console.log(JSON.stringify([...outIterable]));
61
- // [3] => [{"index":0,"value":0.19075931906641008},{"index":2,"value":0.4873469062925415},{"index":4,"value":0.4412516774100035}]
62
-
63
- console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
64
- // [4] => Counts: 6, 5, 3, 0.4873469062925415
65
-
66
-
67
- const outArray = [...p1([0.7, 0.5, 0.4, 0.8, 0.3, 1])];
68
- console.log(JSON.stringify(outArray));
69
- // [5] => [{"index":0,"value":0.5},{"index":2,"value":0.3}]
70
-
71
- console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
72
- // [6] => Counts: 6, 3, 2, 0.5
73
-
74
- ```
75
-
76
- Notes:
77
- 1. All sensors are undefined as expected
78
- 2. All sensors are still undefined as `p1(generate(Math.random, 1000))` only returns an `IterableIterator`. IOW no data has flown through the pipe yet.
79
- 3. `[...outIterable]` Is a shorthand way to populate an array with data from an iterable.
80
- 4. The sensors now have values we can peek at!
81
- 5. The pipe `p1` can be reused with new data, this time the input is a simple array
82
- 6. The same sensors will reflect the correct state from the second run
83
-
84
- Further the sensors can be observed at any point during the process.
85
-
86
- ```javascript
87
- for (const row of p1(generate(Math.random, 1000000))) {
88
- console.log(`${row.index}: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
89
- }
90
- // => 0: 1, 1, 1, 0.13662528848681
91
- // => 2: 3, 3, 2, 0.13662528848681
92
- // => 4: 7, 5, 3, 0.4328468228869129
93
- ```
94
-
95
- Note: Even though there is 1000000 rows of data being potentially generated, only 7 are actually read for this run.
96
-
97
- ## API Reference
98
-
99
- * [Activities](#iterable-activities)
100
- * [Sensors](#sensors)
101
- * [Convenience](#convenience)
102
-
103
- ### Activities
104
-
105
- _Functions which alter data inside the dataflow pipe_
106
-
107
- <a name="concat" href="#concat">#</a> **concat**(_iterable_, _iterable_): _iterable_ <br>
108
- <a name="concat" href="#concat">#</a> **concat**(_iterable_): (_iterable_) => _iterable_ <br>
109
-
110
- Concatenates two iterables into a single iterable. Similar to [Array.concat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat).
111
-
112
- ```typescript
113
- concat(["a", "b", "c"], ["d", "e", "f"]); // => "a", "b", "c", "d", "e", "f"
114
-
115
- const concatDEF = concat(["d", "e", "f"]);
116
- concatDEF(["a", "b", "c"]); // => "a", "b", "c", "d", "e", "f"
117
- concatDEF(["1", "2", "3"]); // => "1", "2", "3", "d", "e", "f"
118
- ```
119
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/concat.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/concat.spec.ts)
120
-
121
- <a name="each" href="#each">#</a> **each**(_iterable_, _callbackFn_): _iterable_ <br>
122
- <a name="each" href="#each">#</a> **each**(_callbackFn): (_iterable_) => _iterable_ <br>
123
-
124
- Perform callback for `each` row in an iterable. Cannot alter the iterable value. Similar to [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). Useful for debugging steps in a [pipe](#pipe).
125
-
126
- ```typescript
127
- each(["a", "b", "c"], (row, idx) => console.log(row)); // => "a", "b", "c"
128
-
129
- const logFlow = each(console.log);
130
- logFlow(["a", "b", "c"]); // => "a", "b", "c"
131
- ```
132
-
133
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/each.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/each.spec.ts)
134
-
135
- <a name="entries" href="#entries">#</a> **entries**(_iterable_): _iterable_ <br>
136
- <a name="entries" href="#entries">#</a> **entries**(): (_iterable_) => _iterable_ <br>
137
-
138
- Perform callback for `entries` row in an iterable. Cannot alter the iterable value. Similar to [Array.entries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries).
139
-
140
- ```typescript
141
- entries(["a", "b", "c"]); // => [0, "a"], [1, "b"], [2, "c"]
142
-
143
- const calcEntries = entries();
144
- calcEntries(["a", "b", "c"]); // => [0, "a"], [1, "b"], [2, "c"]
145
- ```
146
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/entries.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/entries.spec.ts)
147
-
148
- <a name="filter" href="#filter">#</a> **filter**(_iterable_, _condition_): _iterable_ <br>
149
- <a name="filter" href="#filter">#</a> **filter**(_condition_): (_iterable_) => _iterable_ <br>
150
-
151
- Filter iterable based on some `condition`. Similar to [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
152
-
153
- ```typescript
154
- const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
155
-
156
- filter(words, word => word.length > 6); // => "exuberant", "destruction", "present"
157
-
158
- const smallWords = filter(word => word.length <= 6);
159
- smallWords(words); // => "spray", "limit", "elite"
160
- ```
161
-
162
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/filter.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/filter.spec.ts)
163
-
164
- <a name="first" href="#first">#</a> **first**(_iterable_, _number_): _iterable_ <br>
165
- <a name="first" href="#first">#</a> **first**(_number_): (_iterable_) => _iterable_ <br>
166
-
167
- Limit the flow to the first N rows of data.
168
-
169
- ```typescript
170
- const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
171
-
172
- first(words, 3); // => "spray", "limit", "elite"
173
-
174
- const first2 = first(2);
175
- first2(words); // => "spray", "limit"
176
- ```
177
-
178
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/first.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/first.spec.ts)
179
-
180
- <a name="group" href="#group">#</a> **group**(_iterable_, _condition_): _iterable_ <br>
181
- <a name="group" href="#group">#</a> **group**(_condition_): (_iterable_) => _iterable_ <br>
182
-
183
- Groups data based on some grouping condition. Output is in the form {key: groupCondition, value:[...]}, where the key has to be either a `number` or a `string`.
184
-
185
- ```typescript
186
- const words = ["one", "two", "three", "four", "five", "six"];
187
-
188
- group(words, word => word.length); // => {key: 3, value: ["one", "two", "six"]}, {key: 4, value: ["four", "five"]}, { key: 5, value: ["three"]}
189
-
190
- const groupByLength = group(word => word.length);
191
- groupByLength(words); // => {key: 3, value: ["one", "two", "six"]}, {key: 4, value: ["four", "five"]}, { key: 5, value: ["three"]}
192
- ```
193
-
194
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/group.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/group.spec.ts)
195
-
196
- <a name="histogram" href="#histogram">#</a> **histogram**(_iterable_, _condition_, _options_): _iterable_ <br>
197
- <a name="histogram" href="#histogram">#</a> **histogram**(_condition_, _options_): (_iterable_) => _iterable_ <br>
198
-
199
- Groups data into buckets (or bins) based on numeric ranges. Output is in the form `{from: numeric, to: numeric, value:[...]}`.
200
-
201
- Available options are:
202
- ```
203
- { buckets: number } // Specify number of buckets / bins
204
- ```
205
- or
206
- ```
207
- { min: number, range: number } // Specify starting bucket (min) and size of bucket (range)
208
- ```
209
-
210
- ```typescript
211
- const data = [1, 12, 13, 13, 3, 14, 19, 6];
212
- histogram(data, n => n, { buckets: 3 }); // => {"from":1,"to":7,"value":[1,3,6]},{"from":7,"to":13,"value":[12]},{"from":13,"to":19,"value":[13,13,14,19]}
213
-
214
- histogram(data, n => n, { min: 0, range: 5 }); // => {"from":0,"to":5,"value":[1,3]},{"from":5,"to":10,"value":[6]},{"from":10,"to":15,"value":[12,13,13,14]},{"from":15,"to":20,"value":[19]}
215
- ```
216
-
217
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/histogram.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/histogram.spec.ts)
218
-
219
- <a name="map" href="#map">#</a> **map**(_iterable_, _callback_): _iterable_ <br>
220
- <a name="map" href="#map">#</a> **map**(_callback_): (_iterable_) => _iterable_ <br>
221
-
222
- Map data to a new shape via a callback frunction. Similar to [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
223
-
224
- ```typescript
225
- map([{ n: 22 }, { n: 11 }, { n: 33 }], (row, idx) => ({ ...row, index: idx })); // => { n: 22, index: 0 }, { n: 11, index: 1 }, { n: 33, index: 2 }
226
-
227
- const indexData = map((row, idx) => ({ ...row, index: idx + 1 }));
228
- indexData([{ n: 22 }, { n: 11 }, { n: 33 }]); // => { n: 22, index: 1 }, { n: 11, index: 2 }, { n: 33, index: 3 }
229
- ```
230
-
231
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/map.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/map.spec.ts)
232
-
233
- <a name="skip" href="#skip">#</a> **skip**(_iterable_, _number_): _iterable_ <br>
234
- <a name="skip" href="#skip">#</a> **skip**(_number_): (_iterable_) => _iterable_ <br>
235
-
236
- Skip a set number of rows.
237
-
238
- ```typescript
239
- const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
240
-
241
- skip(words, 3); // => "exuberant", "destruction", "present"
242
-
243
- const skip4 = skip(4);
244
- skip4(words); // => "destruction", "present"
245
- ```
246
-
247
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/skip.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/skip.spec.ts)
248
-
249
- <a name="sort" href="#sort">#</a> **sort**(_iterable_, _compare_): _iterable_ <br>
250
- <a name="sort" href="#sort">#</a> **sort**(_compare_): (_iterable_) => _iterable_ <br>
251
-
252
- Sort iterable based on result of `compare` function (should return -1, 0, 1). Similar to [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
253
-
254
-
255
- ```typescript
256
- var numbers = [4, 2, 5, 1, 3];
257
-
258
- sort(numbers, (a, b) => a - b); // => 1, 2, 3, 4, 5
259
-
260
- const reverseSort = sort((a, b) => b - a);
261
- reverseSort(numbers) // => 5, 4, 3, 2, 1
262
- ```
263
-
264
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/sort.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/sort.spec.ts)
265
-
266
- ### Sensors
267
-
268
- _A collection of "Observers" which can be adapted as functions, activities and sensors_
269
-
270
- #### Types / Interfaces
271
-
272
- ```typescript
273
- export interface Observer<T, U> {
274
- observe(r: T, idx: number): void;
275
- peek(): U;
276
- }
277
- ```
278
-
279
- #### Adapters
280
-
281
- <a name="sensor" href="#sensor">#</a> **sensor**(_: _Observer_): _iterable_ <br>
282
-
283
- Adapts an observer so it can be used in a pipe.
284
-
285
- <a name="scalar" href="#scalar">#</a> **scalar**(_: _Observer_): _any_ <br>
286
-
287
- Adapts an observer so it can be called as a regular function.
288
-
289
- #### Observers
290
-
291
- <a name="count" href="#count">#</a> **count**(): _Observer_ <br>
292
-
293
- Counts the number of "observed" rows:
294
-
295
- ```typescript
296
- const s1 = count();
297
- const s2 = count();
298
- const p1 = pipe(
299
- sensor(s1),
300
- filter(r => r.age > 30),
301
- sensor(s2),
302
- );
303
- const data = [...p1(population)];
304
- s1.peek(); // => 1000;
305
- s2.peek(); // => 699;
306
-
307
- const doCount = scalar(count());
308
- doCount([5, 1, 2, -3, 4]); // => 5
309
- ```
310
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/count.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/count.spec.ts)
311
-
312
- <a name="min" href="#min">#</a> **min**(): _Observer_ <br>
313
- <a name="min" href="#min">#</a> **min**(_accessor_): _Observer_ <br>
314
-
315
- Calculates minimal value for "observed" rows:
316
-
317
- ```typescript
318
- const s1 = min();
319
- const s2 = min();
320
- const p1 = pipe(
321
- sensor(s1),
322
- filter(r => r > 3),
323
- sensor(s2),
324
- );
325
- const data = [...p1([1, 2, 3, 4, 5, 0])];
326
- s1.peek() // => 0
327
- s2.peek() // => 4
328
-
329
- const calcMin = scalar(min(row => row.id));
330
- calcMin([{ id: 22 }, { id: 44 }, { id: 33 }]); // => 22
331
- ```
332
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/min.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/min.spec.ts)
333
-
334
- <a name="max" href="#max">#</a> **max**(): _Observer_ <br>
335
- <a name="max" href="#max">#</a> **max**(_accessor_): _Observer_ <br>
336
-
337
- Calculates maximum value for "observed" rows:
338
-
339
- ```typescript
340
- const s1 = max();
341
- const s2 = max();
342
- const p1 = pipe(
343
- sensor(s1),
344
- filter(r => r < 3),
345
- sensor(s2),
346
- );
347
- const data = [...p1([1, 2, 3, 4, 5, 0])];
348
- s1.peek() // => 5
349
- s2.peek() // => 2
350
-
351
- const calcMax = scalar(max(row => row.id));
352
- calcMax([{ id: 22 }, { id: 44 }, { id: 33 }]); // => 44
353
- ```
354
-
355
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/max.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/max.spec.ts)
356
-
357
- <a name="extent" href="#extent">#</a> **extent**(): _Observer_ <br>
358
- <a name="extent" href="#extent">#</a> **extent**(_accessor_): _Observer_ <br>
359
-
360
- Calculates extent (min + max) values for "observed" rows:
361
-
362
- ```typescript
363
- const s1 = extent(r => r.age);
364
- const s2 = extent(r => r.age);
365
- const p1 = pipe(
366
- sensor(s1),
367
- filter(r => r.age > 30),
368
- sensor(s2),
369
- );
370
- const data = [...p1(population)];
371
- s1.peek() // => [16, 66]
372
- s2.peek() // => [31, 66]
373
-
374
- const calcExtent = scalar(extent(row => row.id));
375
- calcExtent([{ id: 22 }, { id: 44 }, { id: 33 }]); // => [22, 44]
376
- ```
377
-
378
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/extent.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/extent.spec.ts)
379
-
380
- <a name="mean" href="#mean">#</a> **mean**(): _Observer_ <br>
381
- <a name="mean" href="#mean">#</a> **mean**(_accessor_): _Observer_ <br>
382
-
383
- Calculates mean (average) value for "observed" rows:
384
-
385
- ```typescript
386
- const calcMean = scalar(mean());
387
- calcMean([5, -6, 1, 2, -2])) // => 0
388
- ```
389
-
390
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/mean.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/mean.spec.ts)
391
-
392
- <a name="median" href="#median">#</a> **median**(): _Observer_ <br>
393
- <a name="median" href="#median">#</a> **median**(_accessor_): _Observer_ <br>
394
-
395
- Calculates median value for "observed" rows:
396
-
397
- ```typescript
398
- const calcMedian = scalar(median());
399
- calcMedian([-6, -2, 1, 2, 5]) // => 1
400
- calcMedian([5, -6, 1, 2, -2]) // => 1
401
- calcMedian([-6, -2, 1, 2, 5, 6]) // => 1.5
402
- calcMedian([5, -6, 1, 2, -2, 6]) // => 1.5
403
- calcMedian([9]) // => 9
404
- ```
405
-
406
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/median.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/median.spec.ts)
407
-
408
- <a name="quartile" href="#quartile">#</a> **quartile**(): _Observer_ <br>
409
- <a name="quartile" href="#quartile">#</a> **quartile**(_accessor_): _Observer_ <br>
410
-
411
- Calculates quartile value for "observed" rows:
412
-
413
- ```typescript
414
- const calcQuartile = scalar(quartile());
415
- calcQuartile([6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]) // => [6, 15, 40, 43, 49]
416
- calcQuartile([7, 15, 36, 39, 40, 41]) // => [7, 15, 37.5, 40, 41]
417
- calcQuartile([1, 22, 133]) // => [1, 1, 22, 133, 133]
418
- calcQuartile([2, 144, 33]) // => [2, 2, 33, 144, 144]
419
- ```
420
-
421
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/quartile.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/quartile.spec.ts)
422
-
423
- <a name="reduce" href="#reduce">#</a> **reduce**(_reducer_[, _initialValue_]): _Observer_ <br>
424
-
425
- Calculates reduced value for "observed" rows:
426
-
427
- ```typescript
428
- const reduceFunc = (prev, row) => prev + row;
429
- const calcReduce1 = scalar(reduce(reduceFunc));
430
- const calcReduce2 = scalar(reduce(reduceFunc), 10);
431
-
432
- calcReduce1([1, 2, 3, 4, 5]) // => 15
433
- calcReduce2([1, 2, 3, 4, 5]) // => 25
434
- ```
435
-
436
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/reduce.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/reduce.spec.ts)
437
-
438
- <a name="variance" href="#variance">#</a> **variance**(): _Observer_ <br>
439
- <a name="variance" href="#variance">#</a> **variance**(_accessor_): _Observer_ <br>
440
-
441
- Calculates the [variance](https://en.wikipedia.org/wiki/Variance) for the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
442
-
443
- ```typescript
444
- const calcVariance = scalar(variance());
445
- calcVariance([5, 1, 2, 3, 4]) // => 2.5
446
- ```
447
-
448
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/variance.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/variance.spec.ts)
449
-
450
- <a name="deviation" href="#deviation">#</a> **deviation**(): _Observer_ <br>
451
- <a name="deviation" href="#deviation">#</a> **deviation**(_accessor_): _Observer_ <br>
452
-
453
- Calculates the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) for the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
454
-
455
- ```typescript
456
- const calcDeviation = scalar(deviation());
457
- calcDeviation([5, 1, 2, 3, 4]) // => 1.58113883008 == sqrt(2.5)
458
- ```
459
-
460
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/deviation.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/deviation.spec.ts)
461
-
462
- <a name="distribution" href="#distribution">#</a> **distribution**(): _Observer_<_number_, { min: _number_, mean: _number_, max: _number_, deviation: _number_, variance: _number_}> <br>
463
- <a name="distribution" href="#distribution">#</a> **distribution**(_accessor_): _Observer_<_any_, { min: _number_, mean: _number_, max: _number_, deviation: _number_, variance: _number_}> <br>
464
-
465
- Calculates a "distribution" (a combination of min, max, mean, variance and deviance) of the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
466
-
467
- ```typescript
468
- const calcDistribution = scalar(distribution());
469
- calcDistribution([5, 1, 2, 3, 4])) // => { min: 1, mean: 3, max: 5, deviation: Math.sqrt(2.5), variance: 2.5}
470
- ```
471
-
472
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/distribution.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/distribution.spec.ts)
473
-
474
- ### Convenience
475
-
476
- _Convenience functions_
477
-
478
- <a name="pipe" href="#pipe">#</a> **pipe**(_iterable_, ..._iterableActivity_): _iterable_ <br>
479
- <a name="pipe" href="#pipe">#</a> **pipe**(_iterable_, ..._iterableActivity_, _scalarActivity_): _scalar_ <br>
480
- <a name="pipe" href="#pipe">#</a> **pipe**(..._iterableActivity_): _iterableActivity_ <br>
481
- <a name="pipe" href="#pipe">#</a> **pipe**(..._iterableActivity_, _scalarActivity_): _scalarActivity_ <br>
482
-
483
- Pipes a series of activities into a single process pipeline.
484
-
485
- ```typescript
486
- // Iterable output
487
- pipe([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
488
- filter(n => n <= 5),
489
- map((n, idx) => ({ index: idx, value: n })),
490
- filter(row => row.index % 2 === 0),
491
- sort((l, r) => l.value - r.value),
492
- first(3)
493
- ); // => { index: 0, value: 0 }, { index: 2, value: 2 }, { index: 4, value: 4 }
494
-
495
- const process = pipe(
496
- filter(n => n <= 5),
497
- map((n, idx) => ({ index: idx, value: n })),
498
- filter(row => row.index % 2 === 0),
499
- sort((l, r) => l.value - r.value),
500
- first(3)
501
- );
502
- process([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])// => { index: 0, value: 0 }, { index: 2, value: 2 }, { index: 4, value: 4 }
503
-
504
- // Scalar output
505
- pipe([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
506
- process,
507
- max(row => row.value)
508
- ); // => 4
509
-
510
- const process_2 = pipe(
511
- process,
512
- min(row => row.value)
513
- );
514
- process_2([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // => 0
515
- ```
516
-
517
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/utils/pipe.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/pipe.spec.ts)
518
-
519
- <a name="generate" href="#generate">#</a> **generate**(_generatorFn_[, _maxIterations_]): _iterable_ <br>
520
-
521
- Generates an iterable data set. Optionally limits the length to `maxIterations`.
522
-
523
- ```typescript
524
- // Iterable output
525
- generate(Math.random); // => Random number iterator
526
- generate(Math.random, 100); // => Random number iterator limited to 100 items
527
-
528
- ```
529
-
530
- [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/utils/generate.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/generate.spec.ts)
1
+ # @hpcc-js/dataflow
2
+
3
+ _A small functional library for processing "data flows" in JavaScript ([more examples on ObservableHQ](https://observablehq.com/@gordonsmith/hpcc-js-dataflow)). Highlights:_
4
+
5
+ * **Lazy Evaluation** - Implemented using modern JavaScript generators and iterators
6
+ * **Memory Efficient** - Data "streams" from one "activity" to the next
7
+ * **Functional** - Pure functional implementation.
8
+ * **Fully Typed** - Written in typescript and supports typed chaining of functional activities
9
+ * **UMD/ES6 Bundles** - Works in NodeJS / Browser and includes ES6 modules to ensure you only include what you use (when bundling with RollupJS / Webpack etc.)
10
+
11
+ ## Motivation
12
+
13
+ The underlying motivation for this library is to simplify the processing of data in an efficient way. The analogy we use is one of a "data" pipe, which consists of:
14
+ * Activities: Functional components that modify data as it flows through the pipe.
15
+ * Sensors: Functional components that observe the data as it passes through the pipe.
16
+
17
+ Some other properties of pipes are:
18
+ * Can be defined, before being used.
19
+ * A complex pipe is just another "Activity" and as such can be re-used inside other pipes.
20
+ * Encourages the user to only iterate through the source data ONCE allowing for less memory use and better overall performance.
21
+
22
+ ## Terminology
23
+
24
+ * **Activity** - A functional unit of work that is primary used to alter the data (`map`, `filter`, `sort`, ...).
25
+ * **Sensor** - A function which "observes" the data without modifying it (`min`, `max`, `quartile`, ...).
26
+ * **IterableActivity** - An "Activity" which produces an "Iterable" output (`map`, `filter`, `sort`, ...).
27
+ * **ScalarActivity** - An "Activity" which produces a single value (`min`, `max`, `reduce`...).
28
+ * **Process** or **Pipeline** - A series of "Activities" chained together, so that "data" "flows" through the process / pipeline.
29
+
30
+ ## Quick Example
31
+ _Simple example of data flowing through a `pipe` of activities: _`filter`->`map`->`filter`->`first`_
32
+
33
+ ```javascript
34
+ import { count, filter, first, generate, map, max, pipe, sensor } from "@hpcc-js/dataflow";
35
+
36
+ const c1 = count();
37
+ const c2 = count();
38
+ const c3 = count();
39
+ const m1 = max(row => row.value);
40
+
41
+ const p1 = pipe(
42
+ sensor(c1), // Keep running count of input
43
+ filter(n => n <= 0.5), // Filter out numbers > 0.5
44
+ sensor(c2), // Keep running count of filtered rows
45
+ map((n, idx) => // Convert to JSON Object
46
+ ({ index: idx, value: n })),
47
+ filter(row => row.index % 2 === 0), // Filter even row indecies
48
+ sensor(c3), // Keep running count of final rows
49
+ sensor(m1), // Track largest value
50
+ first(3) // Take first 3 rows
51
+ );
52
+
53
+ console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}`);
54
+ // [1] => Counts: undefined, undefined, undefined
55
+
56
+ const outIterable = p1(generate(Math.random, 1000));
57
+ console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}`);
58
+ // [2] => Counts: undefined, undefined, undefined
59
+
60
+ console.log(JSON.stringify([...outIterable]));
61
+ // [3] => [{"index":0,"value":0.19075931906641008},{"index":2,"value":0.4873469062925415},{"index":4,"value":0.4412516774100035}]
62
+
63
+ console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
64
+ // [4] => Counts: 6, 5, 3, 0.4873469062925415
65
+
66
+
67
+ const outArray = [...p1([0.7, 0.5, 0.4, 0.8, 0.3, 1])];
68
+ console.log(JSON.stringify(outArray));
69
+ // [5] => [{"index":0,"value":0.5},{"index":2,"value":0.3}]
70
+
71
+ console.log(`Counts: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
72
+ // [6] => Counts: 6, 3, 2, 0.5
73
+
74
+ ```
75
+
76
+ Notes:
77
+ 1. All sensors are undefined as expected
78
+ 2. All sensors are still undefined as `p1(generate(Math.random, 1000))` only returns an `IterableIterator`. IOW no data has flown through the pipe yet.
79
+ 3. `[...outIterable]` Is a shorthand way to populate an array with data from an iterable.
80
+ 4. The sensors now have values we can peek at!
81
+ 5. The pipe `p1` can be reused with new data, this time the input is a simple array
82
+ 6. The same sensors will reflect the correct state from the second run
83
+
84
+ Further the sensors can be observed at any point during the process.
85
+
86
+ ```javascript
87
+ for (const row of p1(generate(Math.random, 1000000))) {
88
+ console.log(`${row.index}: ${c1.peek()}, ${c2.peek()}, ${c3.peek()}, ${m1.peek()}`);
89
+ }
90
+ // => 0: 1, 1, 1, 0.13662528848681
91
+ // => 2: 3, 3, 2, 0.13662528848681
92
+ // => 4: 7, 5, 3, 0.4328468228869129
93
+ ```
94
+
95
+ Note: Even though there is 1000000 rows of data being potentially generated, only 7 are actually read for this run.
96
+
97
+ ## API Reference
98
+
99
+ * [Activities](#iterable-activities)
100
+ * [Sensors](#sensors)
101
+ * [Convenience](#convenience)
102
+
103
+ ### Activities
104
+
105
+ _Functions which alter data inside the dataflow pipe_
106
+
107
+ <a name="concat" href="#concat">#</a> **concat**(_iterable_, _iterable_): _iterable_ <br>
108
+ <a name="concat" href="#concat">#</a> **concat**(_iterable_): (_iterable_) => _iterable_ <br>
109
+
110
+ Concatenates two iterables into a single iterable. Similar to [Array.concat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat).
111
+
112
+ ```typescript
113
+ concat(["a", "b", "c"], ["d", "e", "f"]); // => "a", "b", "c", "d", "e", "f"
114
+
115
+ const concatDEF = concat(["d", "e", "f"]);
116
+ concatDEF(["a", "b", "c"]); // => "a", "b", "c", "d", "e", "f"
117
+ concatDEF(["1", "2", "3"]); // => "1", "2", "3", "d", "e", "f"
118
+ ```
119
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/concat.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/concat.spec.ts)
120
+
121
+ <a name="each" href="#each">#</a> **each**(_iterable_, _callbackFn_): _iterable_ <br>
122
+ <a name="each" href="#each">#</a> **each**(_callbackFn): (_iterable_) => _iterable_ <br>
123
+
124
+ Perform callback for `each` row in an iterable. Cannot alter the iterable value. Similar to [Array.forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach). Useful for debugging steps in a [pipe](#pipe).
125
+
126
+ ```typescript
127
+ each(["a", "b", "c"], (row, idx) => console.log(row)); // => "a", "b", "c"
128
+
129
+ const logFlow = each(console.log);
130
+ logFlow(["a", "b", "c"]); // => "a", "b", "c"
131
+ ```
132
+
133
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/each.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/each.spec.ts)
134
+
135
+ <a name="entries" href="#entries">#</a> **entries**(_iterable_): _iterable_ <br>
136
+ <a name="entries" href="#entries">#</a> **entries**(): (_iterable_) => _iterable_ <br>
137
+
138
+ Perform callback for `entries` row in an iterable. Cannot alter the iterable value. Similar to [Array.entries](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/entries).
139
+
140
+ ```typescript
141
+ entries(["a", "b", "c"]); // => [0, "a"], [1, "b"], [2, "c"]
142
+
143
+ const calcEntries = entries();
144
+ calcEntries(["a", "b", "c"]); // => [0, "a"], [1, "b"], [2, "c"]
145
+ ```
146
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/entries.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/entries.spec.ts)
147
+
148
+ <a name="filter" href="#filter">#</a> **filter**(_iterable_, _condition_): _iterable_ <br>
149
+ <a name="filter" href="#filter">#</a> **filter**(_condition_): (_iterable_) => _iterable_ <br>
150
+
151
+ Filter iterable based on some `condition`. Similar to [Array.filter](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter).
152
+
153
+ ```typescript
154
+ const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
155
+
156
+ filter(words, word => word.length > 6); // => "exuberant", "destruction", "present"
157
+
158
+ const smallWords = filter(word => word.length <= 6);
159
+ smallWords(words); // => "spray", "limit", "elite"
160
+ ```
161
+
162
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/filter.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/filter.spec.ts)
163
+
164
+ <a name="first" href="#first">#</a> **first**(_iterable_, _number_): _iterable_ <br>
165
+ <a name="first" href="#first">#</a> **first**(_number_): (_iterable_) => _iterable_ <br>
166
+
167
+ Limit the flow to the first N rows of data.
168
+
169
+ ```typescript
170
+ const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
171
+
172
+ first(words, 3); // => "spray", "limit", "elite"
173
+
174
+ const first2 = first(2);
175
+ first2(words); // => "spray", "limit"
176
+ ```
177
+
178
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/first.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/first.spec.ts)
179
+
180
+ <a name="group" href="#group">#</a> **group**(_iterable_, _condition_): _iterable_ <br>
181
+ <a name="group" href="#group">#</a> **group**(_condition_): (_iterable_) => _iterable_ <br>
182
+
183
+ Groups data based on some grouping condition. Output is in the form {key: groupCondition, value:[...]}, where the key has to be either a `number` or a `string`.
184
+
185
+ ```typescript
186
+ const words = ["one", "two", "three", "four", "five", "six"];
187
+
188
+ group(words, word => word.length); // => {key: 3, value: ["one", "two", "six"]}, {key: 4, value: ["four", "five"]}, { key: 5, value: ["three"]}
189
+
190
+ const groupByLength = group(word => word.length);
191
+ groupByLength(words); // => {key: 3, value: ["one", "two", "six"]}, {key: 4, value: ["four", "five"]}, { key: 5, value: ["three"]}
192
+ ```
193
+
194
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/group.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/group.spec.ts)
195
+
196
+ <a name="histogram" href="#histogram">#</a> **histogram**(_iterable_, _condition_, _options_): _iterable_ <br>
197
+ <a name="histogram" href="#histogram">#</a> **histogram**(_condition_, _options_): (_iterable_) => _iterable_ <br>
198
+
199
+ Groups data into buckets (or bins) based on numeric ranges. Output is in the form `{from: numeric, to: numeric, value:[...]}`.
200
+
201
+ Available options are:
202
+ ```
203
+ { buckets: number } // Specify number of buckets / bins
204
+ ```
205
+ or
206
+ ```
207
+ { min: number, range: number } // Specify starting bucket (min) and size of bucket (range)
208
+ ```
209
+
210
+ ```typescript
211
+ const data = [1, 12, 13, 13, 3, 14, 19, 6];
212
+ histogram(data, n => n, { buckets: 3 }); // => {"from":1,"to":7,"value":[1,3,6]},{"from":7,"to":13,"value":[12]},{"from":13,"to":19,"value":[13,13,14,19]}
213
+
214
+ histogram(data, n => n, { min: 0, range: 5 }); // => {"from":0,"to":5,"value":[1,3]},{"from":5,"to":10,"value":[6]},{"from":10,"to":15,"value":[12,13,13,14]},{"from":15,"to":20,"value":[19]}
215
+ ```
216
+
217
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/histogram.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/histogram.spec.ts)
218
+
219
+ <a name="map" href="#map">#</a> **map**(_iterable_, _callback_): _iterable_ <br>
220
+ <a name="map" href="#map">#</a> **map**(_callback_): (_iterable_) => _iterable_ <br>
221
+
222
+ Map data to a new shape via a callback frunction. Similar to [Array.map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map).
223
+
224
+ ```typescript
225
+ map([{ n: 22 }, { n: 11 }, { n: 33 }], (row, idx) => ({ ...row, index: idx })); // => { n: 22, index: 0 }, { n: 11, index: 1 }, { n: 33, index: 2 }
226
+
227
+ const indexData = map((row, idx) => ({ ...row, index: idx + 1 }));
228
+ indexData([{ n: 22 }, { n: 11 }, { n: 33 }]); // => { n: 22, index: 1 }, { n: 11, index: 2 }, { n: 33, index: 3 }
229
+ ```
230
+
231
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/map.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/map.spec.ts)
232
+
233
+ <a name="skip" href="#skip">#</a> **skip**(_iterable_, _number_): _iterable_ <br>
234
+ <a name="skip" href="#skip">#</a> **skip**(_number_): (_iterable_) => _iterable_ <br>
235
+
236
+ Skip a set number of rows.
237
+
238
+ ```typescript
239
+ const words = ["spray", "limit", "elite", "exuberant", "destruction", "present"];
240
+
241
+ skip(words, 3); // => "exuberant", "destruction", "present"
242
+
243
+ const skip4 = skip(4);
244
+ skip4(words); // => "destruction", "present"
245
+ ```
246
+
247
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/skip.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/skip.spec.ts)
248
+
249
+ <a name="sort" href="#sort">#</a> **sort**(_iterable_, _compare_): _iterable_ <br>
250
+ <a name="sort" href="#sort">#</a> **sort**(_compare_): (_iterable_) => _iterable_ <br>
251
+
252
+ Sort iterable based on result of `compare` function (should return -1, 0, 1). Similar to [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort).
253
+
254
+
255
+ ```typescript
256
+ var numbers = [4, 2, 5, 1, 3];
257
+
258
+ sort(numbers, (a, b) => a - b); // => 1, 2, 3, 4, 5
259
+
260
+ const reverseSort = sort((a, b) => b - a);
261
+ reverseSort(numbers) // => 5, 4, 3, 2, 1
262
+ ```
263
+
264
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/activities/sort.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/sort.spec.ts)
265
+
266
+ ### Sensors
267
+
268
+ _A collection of "Observers" which can be adapted as functions, activities and sensors_
269
+
270
+ #### Types / Interfaces
271
+
272
+ ```typescript
273
+ export interface Observer<T, U> {
274
+ observe(r: T, idx: number): void;
275
+ peek(): U;
276
+ }
277
+ ```
278
+
279
+ #### Adapters
280
+
281
+ <a name="sensor" href="#sensor">#</a> **sensor**(_: _Observer_): _iterable_ <br>
282
+
283
+ Adapts an observer so it can be used in a pipe.
284
+
285
+ <a name="scalar" href="#scalar">#</a> **scalar**(_: _Observer_): _any_ <br>
286
+
287
+ Adapts an observer so it can be called as a regular function.
288
+
289
+ #### Observers
290
+
291
+ <a name="count" href="#count">#</a> **count**(): _Observer_ <br>
292
+
293
+ Counts the number of "observed" rows:
294
+
295
+ ```typescript
296
+ const s1 = count();
297
+ const s2 = count();
298
+ const p1 = pipe(
299
+ sensor(s1),
300
+ filter(r => r.age > 30),
301
+ sensor(s2),
302
+ );
303
+ const data = [...p1(population)];
304
+ s1.peek(); // => 1000;
305
+ s2.peek(); // => 699;
306
+
307
+ const doCount = scalar(count());
308
+ doCount([5, 1, 2, -3, 4]); // => 5
309
+ ```
310
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/count.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/count.spec.ts)
311
+
312
+ <a name="min" href="#min">#</a> **min**(): _Observer_ <br>
313
+ <a name="min" href="#min">#</a> **min**(_accessor_): _Observer_ <br>
314
+
315
+ Calculates minimal value for "observed" rows:
316
+
317
+ ```typescript
318
+ const s1 = min();
319
+ const s2 = min();
320
+ const p1 = pipe(
321
+ sensor(s1),
322
+ filter(r => r > 3),
323
+ sensor(s2),
324
+ );
325
+ const data = [...p1([1, 2, 3, 4, 5, 0])];
326
+ s1.peek() // => 0
327
+ s2.peek() // => 4
328
+
329
+ const calcMin = scalar(min(row => row.id));
330
+ calcMin([{ id: 22 }, { id: 44 }, { id: 33 }]); // => 22
331
+ ```
332
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/min.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/min.spec.ts)
333
+
334
+ <a name="max" href="#max">#</a> **max**(): _Observer_ <br>
335
+ <a name="max" href="#max">#</a> **max**(_accessor_): _Observer_ <br>
336
+
337
+ Calculates maximum value for "observed" rows:
338
+
339
+ ```typescript
340
+ const s1 = max();
341
+ const s2 = max();
342
+ const p1 = pipe(
343
+ sensor(s1),
344
+ filter(r => r < 3),
345
+ sensor(s2),
346
+ );
347
+ const data = [...p1([1, 2, 3, 4, 5, 0])];
348
+ s1.peek() // => 5
349
+ s2.peek() // => 2
350
+
351
+ const calcMax = scalar(max(row => row.id));
352
+ calcMax([{ id: 22 }, { id: 44 }, { id: 33 }]); // => 44
353
+ ```
354
+
355
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/max.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/max.spec.ts)
356
+
357
+ <a name="extent" href="#extent">#</a> **extent**(): _Observer_ <br>
358
+ <a name="extent" href="#extent">#</a> **extent**(_accessor_): _Observer_ <br>
359
+
360
+ Calculates extent (min + max) values for "observed" rows:
361
+
362
+ ```typescript
363
+ const s1 = extent(r => r.age);
364
+ const s2 = extent(r => r.age);
365
+ const p1 = pipe(
366
+ sensor(s1),
367
+ filter(r => r.age > 30),
368
+ sensor(s2),
369
+ );
370
+ const data = [...p1(population)];
371
+ s1.peek() // => [16, 66]
372
+ s2.peek() // => [31, 66]
373
+
374
+ const calcExtent = scalar(extent(row => row.id));
375
+ calcExtent([{ id: 22 }, { id: 44 }, { id: 33 }]); // => [22, 44]
376
+ ```
377
+
378
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/extent.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/extent.spec.ts)
379
+
380
+ <a name="mean" href="#mean">#</a> **mean**(): _Observer_ <br>
381
+ <a name="mean" href="#mean">#</a> **mean**(_accessor_): _Observer_ <br>
382
+
383
+ Calculates mean (average) value for "observed" rows:
384
+
385
+ ```typescript
386
+ const calcMean = scalar(mean());
387
+ calcMean([5, -6, 1, 2, -2])) // => 0
388
+ ```
389
+
390
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/mean.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/mean.spec.ts)
391
+
392
+ <a name="median" href="#median">#</a> **median**(): _Observer_ <br>
393
+ <a name="median" href="#median">#</a> **median**(_accessor_): _Observer_ <br>
394
+
395
+ Calculates median value for "observed" rows:
396
+
397
+ ```typescript
398
+ const calcMedian = scalar(median());
399
+ calcMedian([-6, -2, 1, 2, 5]) // => 1
400
+ calcMedian([5, -6, 1, 2, -2]) // => 1
401
+ calcMedian([-6, -2, 1, 2, 5, 6]) // => 1.5
402
+ calcMedian([5, -6, 1, 2, -2, 6]) // => 1.5
403
+ calcMedian([9]) // => 9
404
+ ```
405
+
406
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/median.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/median.spec.ts)
407
+
408
+ <a name="quartile" href="#quartile">#</a> **quartile**(): _Observer_ <br>
409
+ <a name="quartile" href="#quartile">#</a> **quartile**(_accessor_): _Observer_ <br>
410
+
411
+ Calculates quartile value for "observed" rows:
412
+
413
+ ```typescript
414
+ const calcQuartile = scalar(quartile());
415
+ calcQuartile([6, 7, 15, 36, 39, 40, 41, 42, 43, 47, 49]) // => [6, 15, 40, 43, 49]
416
+ calcQuartile([7, 15, 36, 39, 40, 41]) // => [7, 15, 37.5, 40, 41]
417
+ calcQuartile([1, 22, 133]) // => [1, 1, 22, 133, 133]
418
+ calcQuartile([2, 144, 33]) // => [2, 2, 33, 144, 144]
419
+ ```
420
+
421
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/quartile.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/quartile.spec.ts)
422
+
423
+ <a name="reduce" href="#reduce">#</a> **reduce**(_reducer_[, _initialValue_]): _Observer_ <br>
424
+
425
+ Calculates reduced value for "observed" rows:
426
+
427
+ ```typescript
428
+ const reduceFunc = (prev, row) => prev + row;
429
+ const calcReduce1 = scalar(reduce(reduceFunc));
430
+ const calcReduce2 = scalar(reduce(reduceFunc), 10);
431
+
432
+ calcReduce1([1, 2, 3, 4, 5]) // => 15
433
+ calcReduce2([1, 2, 3, 4, 5]) // => 25
434
+ ```
435
+
436
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/reduce.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/reduce.spec.ts)
437
+
438
+ <a name="variance" href="#variance">#</a> **variance**(): _Observer_ <br>
439
+ <a name="variance" href="#variance">#</a> **variance**(_accessor_): _Observer_ <br>
440
+
441
+ Calculates the [variance](https://en.wikipedia.org/wiki/Variance) for the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
442
+
443
+ ```typescript
444
+ const calcVariance = scalar(variance());
445
+ calcVariance([5, 1, 2, 3, 4]) // => 2.5
446
+ ```
447
+
448
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/variance.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/variance.spec.ts)
449
+
450
+ <a name="deviation" href="#deviation">#</a> **deviation**(): _Observer_ <br>
451
+ <a name="deviation" href="#deviation">#</a> **deviation**(_accessor_): _Observer_ <br>
452
+
453
+ Calculates the [standard deviation](https://en.wikipedia.org/wiki/Standard_deviation) for the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
454
+
455
+ ```typescript
456
+ const calcDeviation = scalar(deviation());
457
+ calcDeviation([5, 1, 2, 3, 4]) // => 1.58113883008 == sqrt(2.5)
458
+ ```
459
+
460
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/deviation.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/deviation.spec.ts)
461
+
462
+ <a name="distribution" href="#distribution">#</a> **distribution**(): _Observer_<_number_, { min: _number_, mean: _number_, max: _number_, deviation: _number_, variance: _number_}> <br>
463
+ <a name="distribution" href="#distribution">#</a> **distribution**(_accessor_): _Observer_<_any_, { min: _number_, mean: _number_, max: _number_, deviation: _number_, variance: _number_}> <br>
464
+
465
+ Calculates a "distribution" (a combination of min, max, mean, variance and deviance) of the "observed" rows. If the number of rows is fewer than two numbers, returns undefined.
466
+
467
+ ```typescript
468
+ const calcDistribution = scalar(distribution());
469
+ calcDistribution([5, 1, 2, 3, 4])) // => { min: 1, mean: 3, max: 5, deviation: Math.sqrt(2.5), variance: 2.5}
470
+ ```
471
+
472
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/observers/distribution.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/distribution.spec.ts)
473
+
474
+ ### Convenience
475
+
476
+ _Convenience functions_
477
+
478
+ <a name="pipe" href="#pipe">#</a> **pipe**(_iterable_, ..._iterableActivity_): _iterable_ <br>
479
+ <a name="pipe" href="#pipe">#</a> **pipe**(_iterable_, ..._iterableActivity_, _scalarActivity_): _scalar_ <br>
480
+ <a name="pipe" href="#pipe">#</a> **pipe**(..._iterableActivity_): _iterableActivity_ <br>
481
+ <a name="pipe" href="#pipe">#</a> **pipe**(..._iterableActivity_, _scalarActivity_): _scalarActivity_ <br>
482
+
483
+ Pipes a series of activities into a single process pipeline.
484
+
485
+ ```typescript
486
+ // Iterable output
487
+ pipe([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
488
+ filter(n => n <= 5),
489
+ map((n, idx) => ({ index: idx, value: n })),
490
+ filter(row => row.index % 2 === 0),
491
+ sort((l, r) => l.value - r.value),
492
+ first(3)
493
+ ); // => { index: 0, value: 0 }, { index: 2, value: 2 }, { index: 4, value: 4 }
494
+
495
+ const process = pipe(
496
+ filter(n => n <= 5),
497
+ map((n, idx) => ({ index: idx, value: n })),
498
+ filter(row => row.index % 2 === 0),
499
+ sort((l, r) => l.value - r.value),
500
+ first(3)
501
+ );
502
+ process([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])// => { index: 0, value: 0 }, { index: 2, value: 2 }, { index: 4, value: 4 }
503
+
504
+ // Scalar output
505
+ pipe([0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
506
+ process,
507
+ max(row => row.value)
508
+ ); // => 4
509
+
510
+ const process_2 = pipe(
511
+ process,
512
+ min(row => row.value)
513
+ );
514
+ process_2([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); // => 0
515
+ ```
516
+
517
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/utils/pipe.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/pipe.spec.ts)
518
+
519
+ <a name="generate" href="#generate">#</a> **generate**(_generatorFn_[, _maxIterations_]): _iterable_ <br>
520
+
521
+ Generates an iterable data set. Optionally limits the length to `maxIterations`.
522
+
523
+ ```typescript
524
+ // Iterable output
525
+ generate(Math.random); // => Random number iterator
526
+ generate(Math.random, 100); // => Random number iterator limited to 100 items
527
+
528
+ ```
529
+
530
+ [[source]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/src/utils/generate.ts) [[tests]](https://github.com/hpcc-systems/Visualization/blob/trunk/packages/dataflow/tests/generate.spec.ts)