Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 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
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
457 changes: 441 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 <ruben@verborgh.org>",
"type": "module",
Expand Down
84 changes: 84 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,87 @@ 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${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']);
});
});
});
13 changes: 13 additions & 0 deletions test/IteratorIterator-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {
IteratorIterator,
} from '../dist/asynciterator.js';

describe('IteratorIterator', () => {
it('Should wrap correctly', async () => {
(await new IteratorIterator((function * () {
yield 1;
yield 2;
yield 3;
})()).toArray()).should.deep.equal([1, 2, 3]);
});
});
18 changes: 11 additions & 7 deletions test/SimpleTransformIterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import {
ArrayIterator,
IntegerIterator,
scheduleTask,
MappingIterator,
FilteringIterator,
SkippingIterator,
LimitingIterator,
} from '../dist/asynciterator.js';

import { EventEmitter } from 'events';
Expand Down Expand Up @@ -1110,8 +1114,8 @@ describe('SimpleTransformIterator', () => {
result.on('end', done);
});

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

it('should execute the map function on all items in order', () => {
Expand Down Expand Up @@ -1146,7 +1150,7 @@ describe('SimpleTransformIterator', () => {
});

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

it('should execute the map function on all items in order', () => {
Expand Down Expand Up @@ -1185,7 +1189,7 @@ describe('SimpleTransformIterator', () => {
});

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

it('should execute the filter function on all items in order', () => {
Expand Down Expand Up @@ -1219,7 +1223,7 @@ describe('SimpleTransformIterator', () => {
});

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

it('should execute the filter function on all items in order', () => {
Expand Down Expand Up @@ -1347,7 +1351,7 @@ describe('SimpleTransformIterator', () => {
});

it('should be a SimpleTransformIterator', () => {
result.should.be.an.instanceof(SimpleTransformIterator);
result.should.be.an.instanceof(SkippingIterator);
});

it('should skip the given number of items', () => {
Expand Down Expand Up @@ -1377,7 +1381,7 @@ describe('SimpleTransformIterator', () => {
});

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

it('should take the given number of items', () => {
Expand Down
18 changes: 0 additions & 18 deletions test/TransformIterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
EmptyIterator,
ArrayIterator,
TransformIterator,
wrap,
scheduleTask,
} from '../dist/asynciterator.js';

Expand Down Expand Up @@ -32,23 +31,6 @@ describe('TransformIterator', () => {
instance.should.be.an.instanceof(EventEmitter);
});
});

describe('the result when called through `wrap`', () => {
let instance;
before(() => { instance = wrap(); });

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

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 TransformIterator', () => {
Expand Down
92 changes: 92 additions & 0 deletions test/UnionIterator-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,50 @@ describe('UnionIterator', () => {
});
});

describe('when constructed with an iterator and with autoStart and one source a promise', () => {
let iterator, sourceIterator;
before(() => {
const sources = [Promise.resolve(range(0, 2)), range(3, 6)];
sourceIterator = new ArrayIterator(sources);
sinon.spy(sourceIterator, 'read');
iterator = new UnionIterator(sourceIterator, { autoStart: true });
});

describe('before reading', () => {
it('should have read the sources', () => {
sourceIterator.read.should.have.been.called;
});

it('should not have ended', () => {
iterator.ended.should.be.false;
});

it('should pass errors', () => {
const callback = sinon.spy();
const error = new Error('error');
iterator.once('error', callback);
sourceIterator.emit('error', error);
callback.should.have.been.calledOnce;
callback.should.have.been.calledWith(error);
});
});

describe('after reading', () => {
let items;
before(async () => {
items = (await toArray(iterator)).sort();
});

it('should have emitted all items', () => {
items.should.eql([0, 1, 2, 3, 4, 5, 6]);
});

it('should have ended', () => {
iterator.ended.should.be.true;
});
});
});

describe('when constructed with an iterator and without autoStart', () => {
let iterator, sourceIterator;
before(() => {
Expand Down Expand Up @@ -308,6 +352,54 @@ describe('UnionIterator', () => {
});
});

describe('when constructed with an iterator and without autoStart and one source as a promise', () => {
let iterator, sourceIterator;
before(() => {
const sources = [Promise.resolve(range(0, 2)), range(3, 6)];
sourceIterator = new ArrayIterator(sources);
sinon.spy(sourceIterator, 'read');
iterator = new UnionIterator(sourceIterator, { autoStart: false });
});

describe('before reading', () => {
it('should not have read the sources', () => {
sourceIterator.read.should.not.have.been.called;
});

it('should not have ended', () => {
iterator.ended.should.be.false;
});

it('should pass errors', () => {
const callback = sinon.spy();
const error = new Error('error');
iterator.once('error', callback);
sourceIterator.emit('error', error);
callback.should.have.been.calledOnce;
callback.should.have.been.calledWith(error);
});
});

describe('after reading', () => {
let items;
before(async () => {
items = (await toArray(iterator)).sort();
});

it('should have read the sources', () => {
sourceIterator.read.should.have.been.called;
});

it('should have emitted all items', () => {
items.should.eql([0, 1, 2, 3, 4, 5, 6]);
});

it('should have ended', () => {
iterator.ended.should.be.true;
});
});
});

describe('a UnionIterator with two sources', () => {
let iterator, sources;

Expand Down
Loading