Skip to content

Commit e9e5b4e

Browse files
committed
Implement Iterator#zipLongest
Creates an iterator that aggregates elements from each of the iterators. If the iterables are of uneven length, missing values are filled-in with undefined. Iteration continues until the longest iterable is exhausted.
1 parent ca1c520 commit e9e5b4e

File tree

5 files changed

+139
-6
lines changed

5 files changed

+139
-6
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@ and this project adheres to
88

99
## [Unreleased][unreleased]
1010

11+
### Added
12+
13+
- `Iterator#zipLongest()` and `Iterator.zipLongest()` similar to `Iterator#zip`
14+
but instead of stopping when shortest iterator is exhausted continues until
15+
the longest iterable is exhausted filling-in missing values with `undefined`.
16+
1117
## [2.2.0][] - 2020-07-10
1218

1319
### Added

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ $ npm install @metarhia/common
139139
- [Iterator](#class-iterator)
140140
- [Iterator.range](#iteratorrangestart-stop-step)
141141
- [Iterator.zip](#iteratorzipiterators)
142+
- [Iterator.zipLongest](#iteratorziplongestiterators)
142143
- [Iterator.prototype.constructor](#iteratorprototypeconstructorbase)
143144
- [Iterator.prototype.apply](#iteratorprototypeapplyfn)
144145
- [Iterator.prototype.chain](#iteratorprototypechainiterators)
@@ -174,6 +175,7 @@ $ npm install @metarhia/common
174175
- [Iterator.prototype.toArray](#iteratorprototypetoarray)
175176
- [Iterator.prototype.toObject](#iteratorprototypetoobject)
176177
- [Iterator.prototype.zip](#iteratorprototypezipiterators)
178+
- [Iterator.prototype.zipLongest](#iteratorprototypeziplongestiterators)
177179
- [iter](#iterbase)
178180
- [iterEntries](#iterentriesobj)
179181
- [iterKeys](#iterkeysobj)
@@ -1072,6 +1074,18 @@ Returns an iterator of Arrays where the i-th tuple contains the i-th element
10721074
from each of the passed iterators. The iterator stops when the shortest input
10731075
iterable is exhausted.
10741076

1077+
#### Iterator.zipLongest(iterators)
1078+
1079+
- `iterators`: [`<Array>`][array]
1080+
1081+
_Returns:_ `<Iterator>`
1082+
1083+
Creates an iterator that aggregates elements from each of the iterators.
1084+
1085+
If the iterables are of uneven length, missing values are filled-in with
1086+
[`<undefined>`][undefined]. Iteration continues until the longest iterable is
1087+
exhausted.
1088+
10751089
#### Iterator.prototype.constructor(base)
10761090

10771091
#### Iterator.prototype.apply(fn)
@@ -1259,6 +1273,18 @@ Returns an iterator of Arrays where the i-th tuple contains the i-th element
12591273
from each of the passed iterators. The iterator stops when the shortest input
12601274
iterable is exhausted.
12611275

1276+
#### Iterator.prototype.zipLongest(iterators)
1277+
1278+
- `iterators`: [`<Array>`][array]
1279+
1280+
_Returns:_ `<Iterator>`
1281+
1282+
Creates an iterator that aggregates elements from each of the iterators.
1283+
1284+
If the iterables are of uneven length, missing values are filled-in with
1285+
[`<undefined>`][undefined]. Iteration continues until the longest iterable is
1286+
exhausted.
1287+
12621288
### iter(base)
12631289

12641290
### iterEntries(obj)

lib/iterator.js

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,17 @@ class Iterator {
167167
return new ZipIterator(this, iterators);
168168
}
169169

170+
// Creates an iterator that aggregates elements from each of the iterators.
171+
//
172+
// If the iterables are of uneven length, missing values are filled-in
173+
// with <undefined>. Iteration continues until the longest iterable
174+
// is exhausted.
175+
// iterators <Array>
176+
// Returns: <Iterator>
177+
zipLongest(...iterators) {
178+
return new ZipIterator(this, iterators, true);
179+
}
180+
170181
chain(...iterators) {
171182
return new ChainIterator(this, iterators);
172183
}
@@ -346,6 +357,17 @@ class Iterator {
346357
return new ZipIterator(toIterator(base), iterators);
347358
}
348359

360+
// Creates an iterator that aggregates elements from each of the iterators.
361+
//
362+
// If the iterables are of uneven length, missing values are filled-in
363+
// with <undefined>. Iteration continues until the longest iterable
364+
// is exhausted.
365+
// iterators <Array>
366+
// Returns: <Iterator>
367+
static zipLongest(base, ...iterators) {
368+
return new ZipIterator(toIterator(base), iterators, true);
369+
}
370+
349371
// Create iterator iterating over the range
350372
// Signature: start, stop[, step]
351373
// start <number>
@@ -534,26 +556,35 @@ class TakeWhileIterator extends Iterator {
534556
}
535557

536558
class ZipIterator extends Iterator {
537-
constructor(base, iterators) {
559+
constructor(base, iterators, longest = false) {
538560
super(base);
539561
this.iterators = iterators.map(toIterator);
562+
this.longest = longest;
540563
}
541564

542565
next() {
543566
const result = [];
544567

545568
const next = this.base.next();
546-
if (next.done) {
569+
if (next.done && !this.longest) {
547570
return next;
548571
}
549572
result.push(next.value);
573+
let allDone = next.done;
550574

551575
for (const iterator of this.iterators) {
552576
const next = iterator.next();
553-
if (next.done) {
577+
if (next.done && !this.longest) {
554578
return next;
555579
}
556580
result.push(next.value);
581+
allDone = allDone && next.done;
582+
}
583+
584+
if (allDone) {
585+
// Shortcut all consecutive calls to the initial check.
586+
this.longest = false;
587+
return { done: true, value: undefined };
557588
}
558589
return { done: false, value: result };
559590
}

package-lock.json

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

test/iterator.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,76 @@ metatests.test('Iterator.zip with multiple iterators', test => {
315315
test.end();
316316
});
317317

318+
metatests.testSync('Iterator#zipLongest with single iterator', test => {
319+
const it = iter(array).take(1);
320+
const toZip = iter(array).skip(2);
321+
test.strictSame(it.zipLongest(toZip).toArray(), [
322+
[1, 3],
323+
[undefined, 4],
324+
]);
325+
});
326+
327+
metatests.testSync('Iterator#zipLongest with multiple iterators', test => {
328+
const it = iter(array);
329+
const itr = iter(array).take(3);
330+
const iterator = iter(array).take(2);
331+
test.strictSame(it.zipLongest(itr, iterator).toArray(), [
332+
[1, 1, 1],
333+
[2, 2, 2],
334+
[3, 3, undefined],
335+
[4, undefined, undefined],
336+
]);
337+
});
338+
339+
metatests.testSync('Iterator#zipLongest different length', test => {
340+
const it = iter([1, 2, 3]);
341+
test.strictSame(it.zipLongest(iter([1]), iter([1, 2, 3, 4, 5])).toArray(), [
342+
[1, 1, 1],
343+
[2, undefined, 2],
344+
[3, undefined, 3],
345+
[undefined, undefined, 4],
346+
[undefined, undefined, 5],
347+
]);
348+
});
349+
350+
metatests.testSync('Iterator.zipLongest with single iterator', test => {
351+
const it1 = iter(array).take(1);
352+
const it2 = iter(array).skip(2);
353+
test.strictSame(Iterator.zipLongest(it1, it2).toArray(), [
354+
[1, 3],
355+
[undefined, 4],
356+
]);
357+
});
358+
359+
metatests.testSync('Iterator.zipLongest with multiple iterators', test => {
360+
const it1 = iter(array);
361+
const it2 = iter(array).take(3);
362+
const it3 = iter(array).take(2);
363+
test.strictSame(Iterator.zipLongest(it1, it2, it3).toArray(), [
364+
[1, 1, 1],
365+
[2, 2, 2],
366+
[3, 3, undefined],
367+
[4, undefined, undefined],
368+
]);
369+
});
370+
371+
metatests.testSync('Iterator.zipLongest different length', test => {
372+
test.strictSame(
373+
Iterator.zipLongest(
374+
iter([1, 2, 3]),
375+
iter([1]),
376+
iter([1, 2, 3, 4, 5])
377+
).toArray(),
378+
[
379+
[1, 1, 1],
380+
[2, undefined, 2],
381+
[3, undefined, 3],
382+
[undefined, undefined, 4],
383+
[undefined, undefined, 5],
384+
]
385+
);
386+
});
387+
318388
metatests.test('Iterator.chain', test => {
319389
const it = iter(array).take(1);
320390
const itr = iter(array)

0 commit comments

Comments
 (0)