Joint Iteration
for Stage 1
Michael Ficarra • September 2023
motivation
current state-of-the-art: write your own helper 🙁
function* zip<A, B>(as: Iterable<A>, bs: Iterable<B>): Iterable<[A, B]> {
const iterA = as[Symbol.iterator]();
const iterB = bs[Symbol.iterator]();
while (true) {
const iterResultA = iterA.next();
const iterResultB = iterB.next();
if (iterResultA.done !== iterResultB.done) {
throw new Error('iterators ended at different times');
}
if (iterResultA.done) {
break;
}
yield [iterResultA.value, iterResultB.value];
}
}
likely shape: static method(s) on Iterator
Iterator.zip(as, bs);
or
Iterator.zip(as, bs, cs, ...);
design space: does zipWith obviate zip?
Iterator.zipWith(Math.max, as, bs, cs, ...);
// Math.max(a0, b0, c0), Math.max(a1, b1, c1), ...
Iterator.zipWith(Array.of, as, bs, cs, ...);
// [a0, b0, c0], [a1, b1, c1], ...
design space: varargs? just 2? Iterable<Iterator>?
Iterator.zip(as, bs);
or
Iterator.zip(as, bs, cs, ...);
or
Iterator.zip([as, bs, cs, ...]);
design space: positional or named iterators?
Iterator.zip({ a: as, b: bs, c: cs });
// { a: a0, b: b0, c: c0 },
// { a: a1, b: b1, c: c1 },
// ...
design space: longest / filled / strict
Iterator.zipLongest(as, bs); // fill with undefined
Iterator.zipFilled(as, bs, filler); // fill with filler
Iterator.zipFilled(as, bs, asFiller, bsFiller);
Iterator.zipStrict(as, bs); // throw
prior art: other languages
language | shortest | longest | privileged | strict | -With | 3+ sources | 1 source | 0 sources |
Clojure | variadic map | | | | yes | yes | yes | |
Elm | | | | | List.map2 | yes | yes? | |
Haskell | zip | | | | zipWith | yes | | |
OCaml | zip | | | | 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 | | | | | | | |
prior art: JS libraries
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 | | | |
wu | zip | zipLongest | | | zipWith | yes | yes | |
zipiterators | | zipiterators | | | | | | |
Champion's Thoughts
Stage 1?