|
1 | 1 | Iterator Chunking
|
2 | 2 | =================
|
3 | 3 |
|
4 |
| -A TC39 proposal to add a method to iterators for producing an iterator of its subsequences. |
| 4 | +A TC39 proposal to consume an iterator as either overlapping or non-overlapping subsequences of configurable size. |
5 | 5 |
|
6 | 6 | **Stage:** 1
|
7 | 7 |
|
8 |
| -See the [January 2024 presentation to committee](https://docs.google.com/presentation/d/1PvU0wOygklWZQUFIZWFLJRyZnFfgd-7LZh6T_z5Ge8g/edit). |
| 8 | +**Specification:** https://tc39.es/proposal-iterator-chunking/ |
| 9 | + |
| 10 | +## presentations to committee |
| 11 | + |
| 12 | +* [January 2024](https://docs.google.com/presentation/d/1PvU0wOygklWZQUFIZWFLJRyZnFfgd-7LZh6T_z5Ge8g) |
| 13 | +* [October 2024](https://docs.google.com/presentation/d/1V2pFMn0s6UIdrjbfaBlfdu9XE4v3u6qD2gBwLRycVr8) |
9 | 14 |
|
10 | 15 | ## motivation
|
11 | 16 |
|
12 | 17 | It can be useful to consume a stream by more than one value at a time. For example, certain algorithms require looking at adjacent elements.
|
13 | 18 |
|
14 |
| -A common solution for this is a `chunks` method that works like the following: |
| 19 | +### chunking |
| 20 | + |
| 21 | +This is commonly solved for non-overlapping subsequences with a "chunking" method that works like the following: |
15 | 22 |
|
16 | 23 | ```js
|
17 |
| -let digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; |
| 24 | +const digits = () => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9].values(); |
18 | 25 |
|
19 |
| -let chunksOf2 = Array.from(digits.values().chunks(2)); |
| 26 | +let chunksOf2 = Array.from(digits().chunks(2)); |
20 | 27 | // [ [0, 1], [2, 3], [4, 5], [6, 7], [8, 9] ]
|
21 | 28 |
|
22 |
| -let chunksOf3 = Array.from(digits.values().chunks(3)); |
| 29 | +let chunksOf3 = Array.from(digits().chunks(3)); |
23 | 30 | // [ [0, 1, 2], [3, 4, 5], [6, 7, 8], [9] ]
|
24 | 31 |
|
25 |
| -let chunksOf4 = Array.from(digits.values().chunks(4)); |
| 32 | +let chunksOf4 = Array.from(digits().chunks(4)); |
26 | 33 | // [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9] ]
|
27 | 34 | ```
|
28 | 35 |
|
29 |
| -A more flexible solution is a sliding window method, usually named `windows`: |
| 36 | +#### use cases for chunking |
30 | 37 |
|
31 |
| -```js |
32 |
| -let windowsOf3 = Array.from(digits.values().windows(3)); |
33 |
| -// [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9] ] |
| 38 | +* pagination |
| 39 | +* columnar/grid layouts, such as calendars |
| 40 | +* batch/stream processing |
| 41 | +* matrix operations |
| 42 | +* formatting/encoding |
| 43 | +* bucketing (using a computed chunk size, for iterators of known size) |
34 | 44 |
|
35 |
| -let windowsOf2AdvancingBy3 = Array.from(digits.values().windows(2, 3)); |
36 |
| -// [ [0, 1], [3, 4], [6, 7], [9] ] |
37 |
| -``` |
| 45 | +### sliding window |
38 | 46 |
|
39 |
| -`chunks` is just a specialisation of `windows` where `chunks(n)` is equivalent to `windows(n, n)`. |
| 47 | +When overlapping sequences are needed, this is commonly called a "sliding window". |
40 | 48 |
|
41 | 49 | ```js
|
42 |
| -let chunksOf4 = Array.from(digits.values().windows(4, 4)); |
43 |
| -// [ [0, 1, 2, 3], [4, 5, 6, 7], [8, 9] ] |
| 50 | +let windowsOf2 = Array.from(digits().windows(2)); |
| 51 | +// [ [0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6], [6, 7], [7, 8], [8, 9] ] |
| 52 | + |
| 53 | +let windowsOf3 = Array.from(digits().windows(3)); |
| 54 | +// [ [0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7], [6, 7, 8], [7, 8, 9] ] |
| 55 | + |
| 56 | +let windowsOf4 = Array.from(digits().windows(4)); |
| 57 | +// [ [0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7], [5, 6, 7, 8], [6, 7, 8, 9] ] |
44 | 58 | ```
|
45 | 59 |
|
46 |
| -## design space |
47 |
| - |
48 |
| -* sliding windows sliding in/out past ends instead of sliding within boundaries? |
49 |
| -* default chunk size to 2? |
50 |
| -* chunking by 0 length? |
51 |
| -* windows advancing by 0? |
52 |
| -* should truncated chunks be returned? |
53 |
| - * filler/padding elements? |
54 |
| -* should windows ever be truncated? |
55 |
| - * should preserve equivalence with chunks, so answer should match chunks |
56 |
| - * only when full iterator doesn't fill a single window? |
57 |
| - * just lock step to 1 for windowing? |
| 60 | +#### use cases for sliding windows |
| 61 | + |
| 62 | +* running/continuous computations, such as averages |
| 63 | +* context-sensitive algorithms, such as pairwise comparisons |
| 64 | +* carousels and their analogues (when applied to an infinite cycle) |
58 | 65 |
|
59 | 66 | ## prior art
|
60 | 67 |
|
|
0 commit comments