a TC39 proposal to synchronise the advancement of multiple iterators
117
stars
87
commits
TypeScript
primary language
May 29, 2026
updated
A TC39 proposal to synchronise the advancement of multiple iterators, often called zip.
Stage: 4. See tc39/ecma262#3802 for the integration PR.
Demo: https://tc39.es/proposal-joint-iteration/demo/
Specification: https://tc39.es/proposal-joint-iteration/
Often you have 2 or more iterators that are positionally aligned (the first
value yielded by the first iterator corresponds to the first value yielded by
the other iterators, and so on), and you would like to operate on the
corresponding values together. A common solution to this is zip, which
produces an iterator of the combined values. zipWith allows combination of
values in some way other than tupling. Some languages express zipWith as a
variadic map.
This proposal adds two methods: Iterator.zip and Iterator.zipKeyed. zip takes an iterable of iterables and produces an iterable of arrays where position corresponds to position in the passed iterable. zipKeyed takes an object whose values are iterables and produces an iterable of objects where keys correspond to keys in the passed object.
Iterator.zip([
[0, 1, 2],
[3, 4, 5],
]).toArray()
/*
Produces:
[
[0, 3],
[1, 4],
[2, 5],
]
*/
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}).toArray()
/*
Produces:
[
{ a: 0, b: 3, c: 7 },
{ a: 1, b: 4, c: 8 },
{ a: 2, b: 5, c: 9 },
]
*/
Both methods take an options bag as a second argument which allows specifying a mode of "shortest" (the default), "longest", or "strict".
For "longest", the options bag can also define padding to be used for shorter inputs by providing an iterable or object (for zip and zipKeyed respectively):
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}, {
mode: 'longest',
padding: { c: 10 },
}).toArray()
/*
Produces:
[
{ a: 0, b: 3, c: 7 },
{ a: 1, b: 4, c: 8 },
{ a: 2, b: 5, c: 9 },
{ a: undefined, b: 6, c: 10 },
];
*/
Iterator.from and flatMap? Decision: Just iterables.
Iterator.from iterates strings; flatMap rejects stringsreturn them? Decision: Yes, except with mode: longest.-With variants for combining the values in other ways than tupling? Decision: Not in this proposal.
zipLongest/zipFilled/zipAll? Decision: Yes, as an option.
this)? Decision: No.zipEqual/zipStrict that throws if they do not complete after the same number of yields? Decision: Yes, as an option.| language | shortest | longest | privileged | strict | -With | 3+ sources | 1 source | 0 sources |
|---|---|---|---|---|---|---|---|---|
| C++ | std::ranges::views::zip | ::zip_transform | yes | yes | ||||
| Clojure | variadic map | yes | yes | yes | ||||
| Elm | List.map2 | yes | yes | |||||
| Haskell | zip | zipWith | yes | |||||
| OCaml | zip | combine | map2 | yes | ||||
| Python | zip | itertools.zip_longest | zip(..., strict=True) | yes | yes | yes, empty | ||
| Ruby | Enumerable#zip | zip | yes | yes | ||||
| Rust | Iterator::zip | |||||||
| Scala | zip | it.zipAll(jt, x, y) | ||||||
| Swift | zip |
| library | shortest | longest | privileged | strict | -With | 3+ sources | 1 source | 0 sources |
|---|---|---|---|---|---|---|---|---|
| @iterable-iterator/zip | zip | zipLongest | yes | yes | ||||
| @softwareventures/iterator | zipOnce | |||||||
| extra-iterable | zip | zip | zip | yes | yes | yes, empty | ||
| immutable.js | Seq::zip | zipWith | yes | yes | ||||
| iter-ops | zip | yes | yes | yes, empty | ||||
| iter-tools | zip | zipAll | yes | yes | yes, empty | |||
| iterablefu | zip | zipAll | yes | yes | yes, empty | |||
| iterare | zip | |||||||
| itertools-ts | zip | zipFilled, zipLongest | zipEqual | yes | yes | yes, empty | ||
| ixjs | zip | yes | yes | yes, empty | ||||
| lodash | zip | zipWith | yes | yes | yes, empty | |||
| ramda | zip | zipWith | ||||||
| sequency | zip | |||||||
| wu | zip | zipLongest | zipWith | yes | yes | |||
| zipiterators | zipiterators |
TypeScript
68.5%
JavaScript
31.5%
a TC39 proposal to synchronise the advancement of multiple iterators
117
stars
87
commits
TypeScript
primary language
May 29, 2026
updated
A TC39 proposal to synchronise the advancement of multiple iterators, often called zip.
Stage: 4. See tc39/ecma262#3802 for the integration PR.
Demo: https://tc39.es/proposal-joint-iteration/demo/
Specification: https://tc39.es/proposal-joint-iteration/
Often you have 2 or more iterators that are positionally aligned (the first
value yielded by the first iterator corresponds to the first value yielded by
the other iterators, and so on), and you would like to operate on the
corresponding values together. A common solution to this is zip, which
produces an iterator of the combined values. zipWith allows combination of
values in some way other than tupling. Some languages express zipWith as a
variadic map.
This proposal adds two methods: Iterator.zip and Iterator.zipKeyed. zip takes an iterable of iterables and produces an iterable of arrays where position corresponds to position in the passed iterable. zipKeyed takes an object whose values are iterables and produces an iterable of objects where keys correspond to keys in the passed object.
Iterator.zip([
[0, 1, 2],
[3, 4, 5],
]).toArray()
/*
Produces:
[
[0, 3],
[1, 4],
[2, 5],
]
*/
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}).toArray()
/*
Produces:
[
{ a: 0, b: 3, c: 7 },
{ a: 1, b: 4, c: 8 },
{ a: 2, b: 5, c: 9 },
]
*/
Both methods take an options bag as a second argument which allows specifying a mode of "shortest" (the default), "longest", or "strict".
For "longest", the options bag can also define padding to be used for shorter inputs by providing an iterable or object (for zip and zipKeyed respectively):
Iterator.zipKeyed({
a: [0, 1, 2],
b: [3, 4, 5, 6],
c: [7, 8, 9],
}, {
mode: 'longest',
padding: { c: 10 },
}).toArray()
/*
Produces:
[
{ a: 0, b: 3, c: 7 },
{ a: 1, b: 4, c: 8 },
{ a: 2, b: 5, c: 9 },
{ a: undefined, b: 6, c: 10 },
];
*/
Iterator.from and flatMap? Decision: Just iterables.
Iterator.from iterates strings; flatMap rejects stringsreturn them? Decision: Yes, except with mode: longest.-With variants for combining the values in other ways than tupling? Decision: Not in this proposal.
zipLongest/zipFilled/zipAll? Decision: Yes, as an option.
this)? Decision: No.zipEqual/zipStrict that throws if they do not complete after the same number of yields? Decision: Yes, as an option.| language | shortest | longest | privileged | strict | -With | 3+ sources | 1 source | 0 sources |
|---|---|---|---|---|---|---|---|---|
| C++ | std::ranges::views::zip | ::zip_transform | yes | yes | ||||
| Clojure | variadic map | yes | yes | yes | ||||
| Elm | List.map2 | yes | yes | |||||
| Haskell | zip | zipWith | yes | |||||
| OCaml | zip | combine | map2 | yes | ||||
| Python | zip | itertools.zip_longest | zip(..., strict=True) | yes | yes | yes, empty | ||
| Ruby | Enumerable#zip | zip | yes | yes | ||||
| Rust | Iterator::zip | |||||||
| Scala | zip | it.zipAll(jt, x, y) | ||||||
| Swift | zip |
| library | shortest | longest | privileged | strict | -With | 3+ sources | 1 source | 0 sources |
|---|---|---|---|---|---|---|---|---|
| @iterable-iterator/zip | zip | zipLongest | yes | yes | ||||
| @softwareventures/iterator | zipOnce | |||||||
| extra-iterable | zip | zip | zip | yes | yes | yes, empty | ||
| immutable.js | Seq::zip | zipWith | yes | yes | ||||
| iter-ops | zip | yes | yes | yes, empty | ||||
| iter-tools | zip | zipAll | yes | yes | yes, empty | |||
| iterablefu | zip | zipAll | yes | yes | yes, empty | |||
| iterare | zip | |||||||
| itertools-ts | zip | zipFilled, zipLongest | zipEqual | yes | yes | yes, empty | ||
| ixjs | zip | yes | yes | yes, empty | ||||
| lodash | zip | zipWith | yes | yes | yes, empty | |||
| ramda | zip | zipWith | ||||||
| sequency | zip | |||||||
| wu | zip | zipLongest | zipWith | yes | yes | |||
| zipiterators | zipiterators |
TypeScript
68.5%
JavaScript
31.5%