From cbdba6e21af73c14c7a8339cd4141b6cce5139a1 Mon Sep 17 00:00:00 2001 From: Denys Otrishko Date: Wed, 3 Jul 2019 17:51:29 +0300 Subject: [PATCH 1/2] Add Iterator#chunk to allow splitting iterator values into Arrays --- CHANGELOG.md | 2 ++ README.md | 13 ++++++++++++ lib/iterator.js | 39 ++++++++++++++++++++++++++++++++++++ test/iterator.js | 51 ++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea503443..e6794055 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/README.md b/README.md index 63df9851..5829eed0 100644 --- a/README.md +++ b/README.md @@ -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) @@ -1104,6 +1105,18 @@ _Result:_ '3, -1'; ``` +#### Iterator.prototype.chunk(splitBy) + +- `splitBy`: [``][number]|[``][function] chunk size or + function that takes current iterator value and returns true if new chunk + should be created with it + - `val`: `` current iterator value + - _Returns:_ [``][boolean] true if new chunk should be made, false if + current value +- _Returns:_ `` + +Split iterator into chunks (Arrays). + #### Iterator.prototype.collectTo(CollectionClass) #### Iterator.prototype.collectWith(obj, collector) diff --git a/lib/iterator.js b/lib/iterator.js index be27514a..1c5f6a01 100644 --- a/lib/iterator.js +++ b/lib/iterator.js @@ -156,6 +156,25 @@ class Iterator { return new FlatMapIterator(this, mapper, thisArg); } + // Split iterator into chunks (Arrays). + // splitBy - | chunk size or function that takes + // current iterator value and returns true if new chunk should be + // created with it + // val current iterator value + // Returns: true if new chunk should be made, false if current value + // Returns: + 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); } @@ -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 = { diff --git a/test/iterator.js b/test/iterator.js index 2dd66909..b2c3631e 100644 --- a/test/iterator.js +++ b/test/iterator.js @@ -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)); From 0aa75f5fb88f88038602f1a8992c5d91ea6c1f44 Mon Sep 17 00:00:00 2001 From: Denys Otrishko Date: Fri, 10 Jul 2020 14:11:28 +0300 Subject: [PATCH 2/2] fixup! Add Iterator#chunk to allow splitting iterator values into Arrays --- README.md | 7 ++++--- lib/iterator.js | 6 ++++-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 5829eed0..ea872c4d 100644 --- a/README.md +++ b/README.md @@ -1109,10 +1109,11 @@ _Result:_ - `splitBy`: [``][number]|[``][function] chunk size or function that takes current iterator value and returns true if new chunk - should be created with it + should be created with it. The first iterator value will always be added to + the first chunk even if `splitBy` returned `true`. - `val`: `` current iterator value - - _Returns:_ [``][boolean] true if new chunk should be made, false if - current value + - _Returns:_ [``][boolean] `true` if new chunk should be made, + `false` if current value should be added to the previous chunk. - _Returns:_ `` Split iterator into chunks (Arrays). diff --git a/lib/iterator.js b/lib/iterator.js index 1c5f6a01..1ace670e 100644 --- a/lib/iterator.js +++ b/lib/iterator.js @@ -159,9 +159,11 @@ class Iterator { // Split iterator into chunks (Arrays). // splitBy - | chunk size or function that takes // current iterator value and returns true if new chunk should be - // created with it + // created with it. The first iterator value will always be added to the + // first chunk even if `splitBy` returned `true`. // val current iterator value - // Returns: true if new chunk should be made, false if current value + // Returns: `true` if new chunk should be made, + // `false` if current value should be added to the previous chunk. // Returns: chunk(splitBy) { let splitter = null;