Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Iterator#zipLongest and improve zip description #349

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@ and this project adheres to

## [Unreleased][unreleased]

### Added

- `Iterator#zipLongest()` and `Iterator.zipLongest()`,
`Iterator#zipLongestWith()` and `Iterator.zipLongestWith()` similar to
`Iterator#zip` but instead of stopping when shortest iterator is exhausted
continues until the longest iterable is exhausted filling-in missing values
with `undefined` or provided `defaultValue`.

## [2.2.0][] - 2020-07-10

### Added
Expand Down
70 changes: 68 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ $ npm install @metarhia/common
- [Iterator](#class-iterator)
- [Iterator.range](#iteratorrangestart-stop-step)
- [Iterator.zip](#iteratorzipiterators)
- [Iterator.zipLongest](#iteratorziplongestiterators)
- [Iterator.zipLongestWith](#iteratorziplongestwithdefaultvalue-iterators)
- [Iterator.prototype.constructor](#iteratorprototypeconstructorbase)
- [Iterator.prototype.apply](#iteratorprototypeapplyfn)
- [Iterator.prototype.chain](#iteratorprototypechainiterators)
Expand Down Expand Up @@ -174,6 +176,8 @@ $ npm install @metarhia/common
- [Iterator.prototype.toArray](#iteratorprototypetoarray)
- [Iterator.prototype.toObject](#iteratorprototypetoobject)
- [Iterator.prototype.zip](#iteratorprototypezipiterators)
- [Iterator.prototype.zipLongest](#iteratorprototypeziplongestiterators)
- [Iterator.prototype.zipLongestWith](#iteratorprototypeziplongestwithdefaultvalue-iterators)
- [iter](#iterbase)
- [iterEntries](#iterentriesobj)
- [iterKeys](#iterkeysobj)
Expand Down Expand Up @@ -1062,11 +1066,39 @@ Create iterator iterating over the range

#### Iterator.zip(...iterators)

- `iterators`: [`<Array>`][array]
- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

Create iterator by zipping multiple provided iterators into one
Creates an iterator that aggregates elements from each of the iterators.

Returns an iterator of Arrays where the i-th tuple contains the i-th element
from each of the passed iterators. The iterator stops when the shortest input
iterable is exhausted.

#### Iterator.zipLongest(...iterators)

- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

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>`][undefined]. Iteration continues until the longest iterable is
exhausted.

#### Iterator.zipLongestWith(defaultValue, ...iterators)

- `defaultValue`: `<any>` value to fill-in missing values with
- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

Creates an iterator that aggregates elements from each of the iterators.

If the iterables are of uneven length, missing values are filled-in with
`<defaultValue>`. Iteration continues until the longest iterable is exhausted.

#### Iterator.prototype.constructor(base)

Expand Down Expand Up @@ -1245,6 +1277,40 @@ offer.

#### Iterator.prototype.zip(...iterators)

- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

Creates an iterator that aggregates elements from each of the iterators.

Returns an iterator of Arrays where the i-th tuple contains the i-th element
from each of the passed iterators. The iterator stops when the shortest input
iterable is exhausted.

#### Iterator.prototype.zipLongest(...iterators)

- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

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>`][undefined]. Iteration continues until the longest iterable is
exhausted.

#### Iterator.prototype.zipLongestWith(defaultValue, ...iterators)

- `defaultValue`: `<any>` value to fill-in missing values with
- `...iterators`: [`<Array>`][array] iterators to be aggregated

_Returns:_ `<Iterator>`

Creates an iterator that aggregates elements from each of the iterators.

If the iterables are of uneven length, missing values are filled-in with
`<defaultValue>`. Iteration continues until the longest iterable is exhausted.

### iter(base)

### iterEntries(obj)
Expand Down
94 changes: 91 additions & 3 deletions lib/iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,40 @@ class Iterator {
return new FlatMapIterator(this, mapper, thisArg);
}

// Creates an iterator that aggregates elements from each of the iterators.
//
// Returns an iterator of Arrays where the i-th tuple contains
// the i-th element from each of the passed iterators.
// The iterator stops when the shortest input iterable is exhausted.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here and in other places where you're mentioning the iterable protocol in combination with "exhausting", you should use the iterator contract instead since iterable only requires for an object to have a function that returns an iterator, while the iterator is an actual object that has a next() method and that can be "exhausted" by calling that method.

Suggested change
// The iterator stops when the shortest input iterable is exhausted.
// The iterator stops when the shortest input iterator is exhausted.

// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
zip(...iterators) {
return new ZipIterator(this, iterators);
}

// 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.
// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
zipLongest(...iterators) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should allow specifying the value that should be used to fill with when there are no values left in one of the iterators.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I couldn't devise a way to make a proper API.

return new ZipIteratorLongest(this, iterators);
}

// Creates an iterator that aggregates elements from each of the iterators.
//
// If the iterables are of uneven length, missing values are filled-in
// with <defaultValue>. Iteration continues until the longest iterable
// is exhausted.
// defaultValue <any> value to fill-in missing values with
// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
zipLongestWith(defaultValue, ...iterators) {
return new ZipIteratorLongest(this, iterators, defaultValue);
}

chain(...iterators) {
return new ChainIterator(this, iterators);
}
Expand Down Expand Up @@ -328,14 +358,40 @@ class Iterator {
return iter(res && res[Symbol.iterator] ? res : [res]);
}

// Create iterator by zipping multiple provided iterators into one
// Signature: ...iterators
// iterators <Array>
// Creates an iterator that aggregates elements from each of the iterators.
//
// Returns an iterator of Arrays where the i-th tuple contains
// the i-th element from each of the passed iterators.
// The iterator stops when the shortest input iterable is exhausted.
// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
static zip(base, ...iterators) {
return new ZipIterator(toIterator(base), iterators);
}

// 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.
// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
static zipLongest(base, ...iterators) {
return new ZipIteratorLongest(toIterator(base), iterators);
}

// Creates an iterator that aggregates elements from each of the iterators.
//
// If the iterables are of uneven length, missing values are filled-in
// with <defaultValue>. Iteration continues until the longest iterable
// is exhausted.
// defaultValue <any> value to fill-in missing values with
// ...iterators <Array> iterators to be aggregated
// Returns: <Iterator>
static zipLongestWith(defaultValue, base, ...iterators) {
return new ZipIteratorLongest(toIterator(base), iterators, defaultValue);
}

// Create iterator iterating over the range
// Signature: start, stop[, step]
// start <number>
Expand Down Expand Up @@ -545,6 +601,38 @@ class ZipIterator extends Iterator {
}
result.push(next.value);
}

return { done: false, value: result };
}
}

class ZipIteratorLongest extends Iterator {
constructor(base, iterators, defaultValue) {
super(base);
this.done = false;
this.iterators = iterators.map(toIterator);
this.defaultValue = defaultValue;
}

next() {
if (this.done) return { done: true, value: undefined };

const result = [];

const next = this.base.next();
result.push(!next.done ? next.value : this.defaultValue);

let allDone = next.done;
for (const iterator of this.iterators) {
const next = iterator.next();
result.push(!next.done ? next.value : this.defaultValue);
allDone = allDone && next.done;
}

if (allDone) {
this.done = true;
return { done: true, value: undefined };
}
return { done: false, value: result };
}
}
Expand Down
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading