Skip to content

Commit

Permalink
iterate faster, pass both state and self to iterator step function
Browse files Browse the repository at this point in the history
  • Loading branch information
andrasq committed Mar 15, 2020
1 parent 88e50a5 commit d994f23
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 20 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -424,7 +424,8 @@ else a Buffer for Buffer data. The callback is invoked when the 'end' event is
Changelog
---------

- 1.6.0 - new function `entries`, new undocumented function `str_locate`, fix get/setIterator property name
- 1.6.0 - new function `entries`, new undocumented function `str_locate`, fix get/setIterator property name,
speed up iterators
- 1.5.1 - fix getProperty, do not prevent multiple callbacks from readBody
- 1.5.0 - new functions `derive`, `varargsRenamed`, `isMethodContext`, `readBody`;
make varargs attach the instance `this` if no `self` given,
Expand Down
28 changes: 16 additions & 12 deletions qibl.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,13 +611,13 @@ function distinct( array ) {
}

// given an traversal state update function that sets this.value and this.done,
// install it on the protype to make instances iterable.
// `step()` should update this.value or this.done as appropriate.
// If this.done is true, this.value is not used.
// create a nodejs iterator to make the instance iterable.
// `step(state, ret)` should set `ret.value` or `ret.done` as appropriate.
// If ret.done is true, ret.value should not be used.
// see qdlist
//
// NOTE: the step function _must_ be a function(), not a () => because
// the latter does not associate with the instance and cannot set this.done.
// NOTE: the step function _must_ be a function() function, not an () => arrow function,
// because the latter does not associate with the instance and cannot set this.done.
//
// Convention: iterators that can be run only once return self,
// those that can be run many times must return a new iterator each time.
Expand All @@ -628,14 +628,18 @@ function distinct( array ) {
// next() returns a data wrapper with properties {value, done}.
// If done is set then value is not to be used.
function makeIterator( step, makeState ) {
return function() {
return function iterator() {
// makeState is passed the object the iterator function is attached to
var state = makeState && makeState(this) || {};
return {
value: null, done: false,
next: function() { this._step(this._state); return this; },
_step: step,
_state: makeState ? makeState(this) : {},
}
value: 0,
done: false,
next: stepIterator,
__step: step,
__state: state,
};
}
function stepIterator() { this.__step(this.__state, this); return this; }
}
// install the iterator as Symbol.iterator if the node version supports symbols, else as ._iterator
function setIterator( obj, iterator ) {
Expand All @@ -662,7 +666,7 @@ function _traverse( obj, transform, target ) {
target.push(transform ? transform(val.value, i) : val.value);
}
} else if (obj && obj.length > 0) {
// this loop mimics Array.from, but transforms quite a bit faster (8x node-v10.15, 14x node-v12)
// this loop mimics Array.from, but transforms quite a bit faster (13x node-v10.15, 15x node-v12)
// testing transform? in the loop is faster than using two custom functions
for (var i = 0; i < obj.length; i++) {
target.push(transform ? transform(obj[i], i) : obj[i]);
Expand Down
29 changes: 22 additions & 7 deletions test-qibl.js
Original file line number Diff line number Diff line change
Expand Up @@ -1337,10 +1337,12 @@ module.exports = {
t.done();
},

'iterator stepper should receive the state object': function(t) {
'iterator stepper should receive the state object and self': function(t) {
var state = {};
var iter = qibl.makeIterator(function stepper(st) {
var iter = qibl.makeIterator(function stepper(st, self) {
t.equal(st, state);
// the self is the same as the function invocation `this`
t.equal(self, this);
t.done();
}, function() { return state });

Expand All @@ -1350,22 +1352,30 @@ module.exports = {
'iterator should be compatible with Array.from': function(t) {
var called = 0;
var a = [1,2,3,4,5];
var iter = qibl.makeIterator(function(state) {
called += 1;
if (state.ix >= state.arr.length) this.done = true;
else this.value = state.arr[state.ix++];
}, function(self) { return { arr: self, ix: 0 } });
var iter = qibl.makeIterator(
function step(state) {
called += 1;
if (state.ix >= state.arr.length) this.done = true;
else this.value = state.arr[state.ix++];
},
function makeState(array) {
return { arr: array, ix: 0 }
}
);
qibl.setIterator(a, iter);

if (typeof Array.from !== 'function') t.skip();

var b = Array.from(a);
t.equal(called, a.length + 1);
t.deepEqual(b, a);
t.equal(called, 6);

called = 0;
var c = [2,4,6];
qibl.setIterator(c, iter);
t.deepEqual(Array.from(c), c);
t.equal(called, 4);

t.done();
},
Expand All @@ -1377,6 +1387,11 @@ module.exports = {
qibl.setIterator(obj, iter);
t.equal(qibl.getIterator(obj), iter);

var iter1 = qibl.getIterator([]);
var iter2 = qibl.getIterator([]);
t.equal(iter1, iter2);
if (typeof Array.from === 'function') t.equal(typeof iter1, 'function');

t.done();
},

Expand Down

0 comments on commit d994f23

Please sign in to comment.