Iterator Chunking
for Stage 2.7
Michael Ficarra • TC39 • May 2025
reminder: problem
consume an iterator as either overlapping or�non-overlapping subsequences of configurable size
solution: consuming non-overlapping subsequences
arrays of length 3:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
an iterator of digits:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
Iterator.prototype.chunks( chunkSize )
solution: consuming overlapping subsequences
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
an iterator of digits:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
arrays of length 4:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
Iterator.prototype.windows( windowSize )
last minute change
remaining open design question
problem: window size > number of yielded values
0 | 1 | 2 |
an iterator of digits:
0 | 1 | 2 | ? |
.windows(4):
options:
context: .chunks() may yield undersized final chunk
arrays of length 3:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
an iterator of digits:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
(+1)
context: behaviour in other languages
language | library / type | function | behaviour |
C++ | std::ranges::views | slide | empty (1) |
Clojure | core | partition | empty (1) |
Elm | List.extra | groupsOfWithStep | empty (1) |
Elm | List.extra | greedyGroupsOfWithStep | undersized (3) |
Haskell | split | divvy | empty (1) |
Java | Stream | Gatherers.windowSliding | undersized (3) |
Kotlin | Iterable | windowed | parameterised ( 1* or 3 ) |
Python | more-itertools | windowed | padded (4) |
Ruby | Enumerable | each_cons | empty (1) |
Rust | Iterator | map_windows | empty (1) |
Rust | slice | windows | empty (1) |
Scala | Seq | sliding | undersized (3) |
* default
context: behaviour in JS libraries
library | type | function | behaviour |
extra-iterable | Iterable | chunk | undersized (3) |
iter-tools | Iterable | window | empty (1) |
iter-tools | Iterable | windowAhead | padded (4) |
itertools-ts | Iterator / Iterable | chunkwiseOverlap | parameterised ( 3* or 1 ) |
Ramda | List | aperture | empty (1) |
* default
champion's preferences
empty (1) seems best and is most common
undersized (3) is also acceptable and aligns with .chunks()
throw (2) is unprecedented and an abuse of exceptions
padded (4) without passing the pad value is very bad
a parameter is fine and flexible, but should there be a default?
separate methods would also be fine
use-case driven
empty (1) or throw (2) or undersized (3)
empty (1) or throw (2)
throw (2) or undersized (3)
should we reconsider
.chunks() behaviour?
assigned Stage 2.7 reviewers
✓ ACE (Ashley Claymore)
✓ JHD (Jordan Harband)
✓ JMN (Jesse Alama)
Stage 2.7?
(assuming a simple solution to the open design question)