Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
35c54b3
adds optimized iterators for mapping (.map()) and filtering (.filter())
jacoscaz Mar 24, 2022
98195c2
adds simpler and faster iterators for skipping and limiting
jacoscaz Mar 24, 2022
f1e4b14
fixes if vs. while in LimitingIterator's .read()
jacoscaz Mar 24, 2022
974f076
refactors read() functions into class methods
jacoscaz Mar 26, 2022
728f9ba
centralizes common code into shared base class
jacoscaz Mar 26, 2022
c1d4735
chore: remove unecessary commented out lines
jeswr Mar 26, 2022
1ec39fb
feat: optimize chained mapping and filtering
jeswr Mar 27, 2022
6141346
fixes missing readable events
jacoscaz Mar 28, 2022
d7ab7d9
synchronously emits readable and error events in SychronousTransformI…
jacoscaz Mar 28, 2022
44fa99c
faster wrapping of iterator-like sources
jacoscaz Mar 25, 2022
e8f9108
adds missing readable event handler in WrappingIterator
jacoscaz Mar 25, 2022
46c6877
puts iterator pass-through behind a dedicated option, fixes breaking …
jacoscaz Mar 26, 2022
60a0d15
better typings for iterator-like and promise-like objects
jacoscaz Mar 26, 2022
12885ba
Allow unionIterator to take promises.
jeswr Mar 26, 2022
730bf64
Release version 3.4.0 of the npm package.
RubenVerborgh Mar 26, 2022
55f21c6
feat: wrapIterator
jeswr Mar 27, 2022
2339f4b
universal wrap() function
jacoscaz Mar 27, 2022
3abf6b7
adds dedicated from* functions for ES2015 Iterator and Iterable, Iter…
jacoscaz Mar 28, 2022
1030891
renames IteratorLike to AsyncIteratorLike to highlight difference fro…
jacoscaz Mar 28, 2022
4bfadd6
chore: SyncUnionIterator
jeswr Mar 27, 2022
c757446
chore: lint
jeswr Mar 27, 2022
3350b2c
changes union() to return an instance of SyncUnionIterator
jacoscaz Mar 30, 2022
14d2f7c
improves handling of BufferedIterator with autoStart set to false in …
jacoscaz Mar 30, 2022
ce929c7
makes union() return UnionIterator instances as we wait for SyncUnion…
jacoscaz Mar 30, 2022
e98e248
introduce maybe map and do some refactoring
jeswr Apr 3, 2022
21e4740
Merge pull request #2 from jeswr/maybe-mapping
jacoscaz Apr 3, 2022
7fa04c4
drops SyncUnionIterator as per plan defined in https://github.com/Rub…
jacoscaz Apr 3, 2022
7a0901a
lazy start of BufferedIterator sources in WrappingIterator and Synchr…
jacoscaz Apr 3, 2022
b354197
immediately closes SynchronousTransformIterator(s) when the source is…
jacoscaz Apr 3, 2022
99a65b0
adds basic tests for MappingIterator
jacoscaz Apr 3, 2022
e956e79
evaluates whether the source is done before readable in SynchronousTr…
jacoscaz Apr 3, 2022
1d1cfc4
adds basic tests for FilteringIterator
jacoscaz Apr 3, 2022
f18e28c
adds basic tests for LimitingIterator
jacoscaz Apr 3, 2022
e08f560
adds basic tests for SkippingIterator
jacoscaz Apr 3, 2022
3789177
adds basic tests for SynchronousTransformIterator, increases coverage…
jacoscaz Apr 3, 2022
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
571 changes: 555 additions & 16 deletions asynciterator.ts

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "asynciterator",
"version": "3.3.0",
"version": "3.4.0",
"description": "An asynchronous iterator library for advanced object pipelines.",
"author": "Ruben Verborgh <[email protected]>",
"type": "module",
Expand Down
93 changes: 93 additions & 0 deletions test/AsyncIterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
ENDED,
DESTROYED,
scheduleTask,
range,
} from '../dist/asynciterator.js';

import { EventEmitter } from 'events';
Expand Down Expand Up @@ -1307,4 +1308,96 @@ describe('AsyncIterator', () => {
});
});
});
describe('Testing chains fo maps and filters', () => {
let iterator;
beforeEach(() => {
iterator = range(0, 2);
});
it('Should handle no transforms', async () => {
iterator.read().should.equal(0);
iterator.read().should.equal(1);
iterator.read().should.equal(2);
});
it('Should handle no transforms arrayified', async () => {
(await iterator.toArray()).should.deep.equal([0, 1, 2]);
});
it('Should apply maps that doubles correctly', async () => {
(await iterator.map(x => x * 2).toArray()).should.deep.equal([0, 2, 4]);
});
it('Should apply maps that doubles correctly', async () => {
(await iterator.map(x => x * 2).maybeMap(x => x === 2 ? null : x * 3).toArray()).should.deep.equal([0, 12]);
});
it('Should apply maps that doubles correctly', async () => {
(await iterator.maybeMap(x => x === 2 ? null : x * 3).toArray()).should.deep.equal([0, 3]);
});
it('Should apply maps that doubles correctly', async () => {
(await iterator.maybeMap(x => x === 2 ? null : x * 3).maybeMap(x => x === 0 ? null : x * 3).toArray()).should.deep.equal([9]);
});
it('Should apply maps that doubles correctly', async () => {
(await iterator.map(x => `x${x}`).toArray()).should.deep.equal(['x0', 'x1', 'x2']);
});
it('Should apply filter correctly', async () => {
(await iterator.filter(x => x % 2 === 0).toArray()).should.deep.equal([0, 2]);
});
it('Should apply filter then map correctly', async () => {
(await iterator.filter(x => x % 2 === 0).map(x => `x${x}`).toArray()).should.deep.equal(['x0', 'x2']);
});
it('Should apply map then filter correctly (1)', async () => {
(await iterator.map(x => x).filter(x => x % 2 === 0).toArray()).should.deep.equal([0, 2]);
});
it('Should apply map then filter to false correctly', async () => {
(await iterator.map(x => `x${x}`).filter(x => true).toArray()).should.deep.equal(['x0', 'x1', 'x2']);
});
it('Should apply map then filter to true correctly', async () => {
(await iterator.map(x => `x${x}`).filter(x => false).toArray()).should.deep.equal([]);
});
it('Should apply filter to false then map correctly', async () => {
(await iterator.filter(x => true).map(x => `x${x}`).toArray()).should.deep.equal(['x0', 'x1', 'x2']);
});
it('Should apply filter to true then map correctly', async () => {
(await iterator.filter(x => false).map(x => `x${x}`).filter(x => false).toArray()).should.deep.equal([]);
});
it('Should apply filter one then double', async () => {
(await iterator.filter(x => x !== 1).map(x => x * 2).toArray()).should.deep.equal([0, 4]);
});
it('Should apply double then filter one', async () => {
(await iterator.map(x => x * 2).filter(x => x !== 1).toArray()).should.deep.equal([0, 2, 4]);
});
it('Should apply map then filter correctly', async () => {
(await iterator.map(x => `x${x}`).filter(x => (x[1] === '0')).toArray()).should.deep.equal(['x0']);
});
it('Should correctly apply 3 filters', async () => {
(await range(0, 5).filter(x => x !== 1).filter(x => x !== 2).filter(x => x !== 2).toArray()).should.deep.equal([0, 3, 4, 5]);
});
it('Should correctly apply 3 maps', async () => {
(await range(0, 1).map(x => x * 2).map(x => `z${x}`).map(x => `y${x}`).toArray()).should.deep.equal(['yz0', 'yz2']);
});
it('Should correctly apply a map, followed by a filter, followed by another map', async () => {
(await range(0, 1).map(x => x * 2).filter(x => x !== 2).map(x => `y${x}`).toArray()).should.deep.equal(['y0']);
});
it('Should correctly apply a filter-map-filter', async () => {
(await range(0, 2).filter(x => x !== 1).map(x => x * 3).filter(x => x !== 6).toArray()).should.deep.equal([0]);
});
it('Should handle transforms', async () => {
iterator = iterator.multiMap(function* (data) {
yield `x${data}`;
yield `y${data}`;
});
(await iterator.toArray()).should.deep.equal(['x0', 'y0', 'x1', 'y1', 'x2', 'y2']);
});
it('Should handle transforms and maps', async () => {
iterator = iterator.multiMap(function* (data) {
yield `x${data}`;
yield `y${data}`;
}).map(x => `z${x}`);
(await iterator.toArray()).should.deep.equal(['zx0', 'zy0', 'zx1', 'zy1', 'zx2', 'zy2']);
});
it('Should handle maps and transforms', async () => {
iterator = iterator.map(x => `z${x}`).multiMap(function* (data) {
yield `x${data}`;
yield `y${data}`;
});
(await iterator.toArray()).should.deep.equal(['xz0', 'yz0', 'xz1', 'yz1', 'xz2', 'yz2']);
});
});
});
62 changes: 62 additions & 0 deletions test/FilteringIterator-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
AsyncIterator,
ArrayIterator,
FilteringIterator,
} from '../dist/asynciterator.js';

import { EventEmitter } from 'events';

describe('FilteringIterator', () => {
describe('The FilteringIterator function', () => {
describe('the result when called with `new`', () => {
let instance;
before(() => {
instance = new FilteringIterator(new ArrayIterator([]), item => true);
});

it('should be a FilteringIterator object', () => {
instance.should.be.an.instanceof(FilteringIterator);
});

it('should be an AsyncIterator object', () => {
instance.should.be.an.instanceof(AsyncIterator);
});

it('should be an EventEmitter object', () => {
instance.should.be.an.instanceof(EventEmitter);
});
});
});

describe('A FilteringIterator', () => {
let iterator, source;
before(() => {
source = new ArrayIterator([0, 1, 2, 3, 4, 5, 6]);
iterator = new FilteringIterator(source, item => item % 2 === 0);
});

describe('when reading items', () => {
const items = [];
before(done => {
iterator.on('data', item => { items.push(item); });
iterator.on('end', done);
});

it('should return items mapped according to the mapping function', () => {
items.should.deep.equal([0, 2, 4, 6]);
});
});
});

describe('A FilteringIterator with a source that emits 0 items', () => {
it('should not return any items', done => {
const items = [];
const iterator = new FilteringIterator(new ArrayIterator([]), () => true);
iterator.on('data', item => { items.push(item); });
iterator.on('end', () => {
items.should.deep.equal([]);
done();
});
});
});
});
29 changes: 29 additions & 0 deletions test/IteratorIterator-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
fromIterator,
fromIterable,
IteratorIterator,
} from '../dist/asynciterator.js';

function *testYield() {
yield 1;
yield 2;
yield 3;
}

describe('IteratorIterator', () => {
it('Should wrap correctly', async () => {
(await new IteratorIterator(testYield()).toArray()).should.deep.equal([1, 2, 3]);
});
});

describe('fromIterator', () => {
it('Should wrap correctly', async () => {
(await fromIterator(testYield()).toArray()).should.deep.equal([1, 2, 3]);
});
});

describe('fromIterable', () => {
it('Should wrap correctly', async () => {
(await fromIterable({ [Symbol.iterator]: testYield }).toArray()).should.deep.equal([1, 2, 3]);
});
});
86 changes: 86 additions & 0 deletions test/LimitingIterator-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import {
AsyncIterator,
ArrayIterator,
LimitingIterator,
} from '../dist/asynciterator.js';

import { EventEmitter } from 'events';

describe('LimitingIterator', () => {
describe('The LimitingIterator function', () => {
describe('the result when called with `new`', () => {
let instance;
before(() => {
instance = new LimitingIterator(new ArrayIterator([]), 10);
});

it('should be a LimitingIterator object', () => {
instance.should.be.an.instanceof(LimitingIterator);
});

it('should be an AsyncIterator object', () => {
instance.should.be.an.instanceof(AsyncIterator);
});

it('should be an EventEmitter object', () => {
instance.should.be.an.instanceof(EventEmitter);
});
});
});

describe('A LimitingIterator', () => {
let iterator, source;
before(() => {
source = new ArrayIterator([0, 1, 2, 3, 4, 5, 6]);
iterator = new LimitingIterator(source, 4);
});

describe('when reading items', () => {
const items = [];
before(done => {
iterator.on('data', item => { items.push(item); });
iterator.on('end', done);
});

it('should return items limited to the specified limit', () => {
items.should.deep.equal([0, 1, 2, 3]);
});
});
});

describe('A LimitingIterator with a source that emits 0 items', () => {
it('should not return any items', done => {
const items = [];
const iterator = new LimitingIterator(new ArrayIterator([]), 10);
iterator.on('data', item => { items.push(item); });
iterator.on('end', () => {
items.should.deep.equal([]);
done();
});
});
});

describe('A LimitingIterator with a limit of 0 items', () => {
it('should not emit any items', done => {
const items = [];
const iterator = new LimitingIterator(new ArrayIterator([0, 1, 2]), 0);
iterator.on('data', item => { items.push(item); });
iterator.on('end', () => {
items.should.deep.equal([]);
done();
});
});
});

describe('A LimitingIterator with a limit of Infinity items', () => {
it('should emit all items', done => {
const items = [];
const iterator = new LimitingIterator(new ArrayIterator([0, 1, 2, 3, 4, 5, 6]), Infinity);
iterator.on('data', item => { items.push(item); });
iterator.on('end', () => {
items.should.deep.equal([0, 1, 2, 3, 4, 5, 6]);
done();
});
});
});
});
62 changes: 62 additions & 0 deletions test/MappingIterator-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import {
AsyncIterator,
ArrayIterator,
MappingIterator,
} from '../dist/asynciterator.js';

import { EventEmitter } from 'events';

describe('MappingIterator', () => {
describe('The MappingIterator function', () => {
describe('the result when called with `new`', () => {
let instance;
before(() => {
instance = new MappingIterator(new ArrayIterator([]), item => item);
});

it('should be a MappingIterator object', () => {
instance.should.be.an.instanceof(MappingIterator);
});

it('should be an AsyncIterator object', () => {
instance.should.be.an.instanceof(AsyncIterator);
});

it('should be an EventEmitter object', () => {
instance.should.be.an.instanceof(EventEmitter);
});
});
});

describe('A MappingIterator', () => {
let iterator, source;
before(() => {
source = new ArrayIterator([0, 1, 2, 3, 4, 5, 6]);
iterator = new MappingIterator(source, item => item * 2);
});

describe('when reading items', () => {
const items = [];
before(done => {
iterator.on('data', item => { items.push(item); });
iterator.on('end', done);
});

it('should return items mapped according to the mapping function', () => {
items.should.deep.equal([0, 2, 4, 6, 8, 10, 12]);
});
});
});

describe('A MappingIterator with a source that emits 0 items', () => {
it('should not return any items', done => {
const items = [];
const iterator = new MappingIterator(new ArrayIterator([]), item => item);
iterator.on('data', item => { items.push(item); });
iterator.on('end', () => {
items.should.deep.equal([]);
done();
});
});
});
});
Loading