Skip to content

Commit

Permalink
Add Iterator#chunk to allow splitting iterator values into Arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
lundibundi committed Jul 10, 2020
1 parent cec1c79 commit cbdba6e
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to
- `Iterator.zip()` - static method for zipping iterators.
- `Iterator#groupBy()` to group iterator value into Map by
specific keys.
- `Iterator#chunks()` to allow splitting iterator by chunk size or
via custom condition into Arrays of values.

### Changed

Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ $ npm install @metarhia/common
- [Iterator.prototype.apply](#iteratorprototypeapplyfn)
- [Iterator.prototype.chain](#iteratorprototypechainiterators)
- [Iterator.prototype.chainApply](#iteratorprototypechainapplyfn)
- [Iterator.prototype.chunk](#iteratorprototypechunksplitby)
- [Iterator.prototype.collectTo](#iteratorprototypecollecttocollectionclass)
- [Iterator.prototype.collectWith](#iteratorprototypecollectwithobj-collector)
- [Iterator.prototype.count](#iteratorprototypecount)
Expand Down Expand Up @@ -1104,6 +1105,18 @@ _Result:_
'3, -1';
```

#### Iterator.prototype.chunk(splitBy)

- `splitBy`: [`<number>`][number]|[`<Function>`][function] chunk size or
function that takes current iterator value and returns true if new chunk
should be created with it
- `val`: `<any>` current iterator value
- _Returns:_ [`<boolean>`][boolean] true if new chunk should be made, false if
current value
- _Returns:_ `<ChunkIterator>`

Split iterator into chunks (Arrays).

#### Iterator.prototype.collectTo(CollectionClass)

#### Iterator.prototype.collectWith(obj, collector)
Expand Down
39 changes: 39 additions & 0 deletions lib/iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,25 @@ class Iterator {
return new FlatMapIterator(this, mapper, thisArg);
}

// Split iterator into chunks (Arrays).
// splitBy - <number> | <Function> chunk size or function that takes
// current iterator value and returns true if new chunk should be
// created with it
// val <any> current iterator value
// Returns: <boolean> true if new chunk should be made, false if current value
// Returns: <ChunkIterator>
chunk(splitBy) {
let splitter = null;
if (typeof splitBy === 'number') {
const num = splitBy;
let counter = 0;
splitter = () => counter++ % num === 0;
} else {
splitter = splitBy;
}
return new ChunkIterator(this, splitter);
}

zip(...iterators) {
return new ZipIterator(this, iterators);
}
Expand Down Expand Up @@ -604,6 +623,26 @@ class SkipWhileIterator extends Iterator {
}
}

class ChunkIterator extends Iterator {
constructor(base, fn) {
super(base);
this.fn = fn;
this.value = [];
}

next() {
let next = this.base.next();
if (next.done && this.value.length === 0) return next;
while (!next.done && (!this.fn(next.value) || this.value.length === 0)) {
this.value.push(next.value);
next = this.base.next();
}
const value = this.value;
this.value = next.done ? [] : [next.value];
return { done: false, value };
}
}

const iter = base => new Iterator(base);

module.exports = {
Expand Down
51 changes: 51 additions & 0 deletions test/iterator.js
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,57 @@ metatests.testSync('Iterator.groupBy thisArg', test => {
);
});

metatests.testSync('Iterator.chunk', test => {
const actual = iter([1, 2, 3, 1, 4, 5, 1, 6, 7])
.chunk(v => v === 1)
.toArray();
test.strictSame(actual, [
[1, 2, 3],
[1, 4, 5],
[1, 6, 7],
]);
});

metatests.testSync('Iterator.chunk with number', test => {
const actual = iter([1, 2, 3, 1, 4, 5, 1, 6, 7])
.chunk(3)
.toArray();
test.strictSame(actual, [
[1, 2, 3],
[1, 4, 5],
[1, 6, 7],
]);
});

metatests.testSync('Iterator.chunk with empty iterator', test => {
const actual = iter([])
.chunk(3)
.toArray();
test.strictSame(actual, []);
});

metatests.testSync('Iterator.chunk with single chunk', test => {
const actual = iter([1, 2, 3, 1, 4, 5, 1, 6, 7])
.chunk(() => false)
.toArray();
test.strictSame(actual, [[1, 2, 3, 1, 4, 5, 1, 6, 7]]);
});

metatests.testSync('Iterator.chunk with empty chunks', test => {
const actual = iter([1, 1, 3, 3, 3, 5, 5])
.chunk(x => x === 3)
.toArray();
test.strictSame(actual, [[1, 1], [3], [3], [3, 5, 5]]);
});

metatests.testSync('Iterator.chunk with all chunks', test => {
const actual = iter([1, 1, 3, 3, 3, 5, 5])
.chunk(() => true)
.toArray();
test.log(actual);
test.strictSame(actual, [[1], [1], [3], [3], [3], [5], [5]]);
});

metatests.testSync('iterEntries must iterate over object entries', test => {
const source = { a: 13, b: 42, c: 'hello' };
test.strictSame(iterEntries(source).toArray(), Object.entries(source));
Expand Down

0 comments on commit cbdba6e

Please sign in to comment.