From 433331adac95cc681e18b07f561bc36bef52799f Mon Sep 17 00:00:00 2001 From: Graeme Yeates Date: Fri, 28 Aug 2015 23:15:02 -0400 Subject: [PATCH] Add ES6 compliant lodash(...).next() iterator. --- lodash.js | 35 +++++++++++++++++++++++ test/test.js | 79 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) diff --git a/lodash.js b/lodash.js index cab1b297f..81407ebdd 100644 --- a/lodash.js +++ b/lodash.js @@ -1492,6 +1492,8 @@ this.__wrapped__ = value; this.__actions__ = actions || []; this.__chain__ = !!chainAll; + this.__index__ = 0; + this.__values__ = undefined; } /** @@ -5867,6 +5869,38 @@ }); }); + /** + * Gets the next value on a wrapped object in ES2015 iterator + * format (). Useful for `for..of` loops and using with + * some ES2015 features. + * + * @name next + * @memberOf _ + * @category Chain + * @returns {*} Returns the next value. + * @example + * + * var wrapped = _([1, 2]); + * + * wrapped.next(); + * // => { 'done': false, 'value': 1 } + * + * wrapped.next(); + * // => { 'done': false, 'value': 2 } + * + * wrapped.next(); + * // => { 'done': true, 'value': undefined } + */ + function wrapperNext() { + if (this.__values__ === undefined) { + this.__values__ = values(this.value()); + } + var done = this.__index__ >= this.__values__.length, + value = done ? undefined : this.__values__[this.__index__++]; + + return { 'done': done, 'value': value }; + } + /** * Creates a clone of the chained sequence planting `value` as the wrapped value. * @@ -11958,6 +11992,7 @@ lodash.prototype.chain = wrapperChain; lodash.prototype.commit = wrapperCommit; lodash.prototype.concat = wrapperConcat; + lodash.prototype.next = wrapperNext; lodash.prototype.plant = wrapperPlant; lodash.prototype.reverse = wrapperReverse; lodash.prototype.toString = wrapperToString; diff --git a/test/test.js b/test/test.js index ae8d32c48..f8407c926 100644 --- a/test/test.js +++ b/test/test.js @@ -16580,6 +16580,85 @@ /*--------------------------------------------------------------------------*/ + QUnit.module('lodash(...).next'); + + _.each([true, false], function(implict) { + function arrayFrom(iter) { + var item; + var result = []; + while (!(item = iter.next()).done) { + result.push(item.value); + } + return result; + } + + function chain(obj) { + return implict ? _(obj) : _.chain(obj); + } + + var chainType = ' in an ' + (implict ? 'implict' : 'explict') + ' chain'; + + test('should produce an ES6 compliant object' + chainType, 7, function() { + var array = [0, 1, 1, 2, 3], + chained = chain(array); + + deepEqual(chained.next(), {done: false, value: 0}); + deepEqual(chained.next(), {done: false, value: 1}); + deepEqual(chained.next(), {done: false, value: 1}); + deepEqual(chained.next(), {done: false, value: 2}); + deepEqual(chained.next(), {done: false, value: 3}); + deepEqual(chained.next(), {done: true, value: undefined}); + deepEqual(chained.next(), {done: true, value: undefined}); + }); + + test('should make a lodash instance act as an iterable' + chainType, 1, function() { + var array = [0, 1, 1, 2, 3, 5, 8, 13, 21], + chained = chain(array); + + deepEqual(arrayFrom(chained), array); + }); + + test('should reset the iterator upon forking (adding an action to the chain)' + chainType, 5, function() { + var array = [0, 1, 1, 2, 3, 5, 8, 13, 21], + chained = chain(array); + + deepEqual(arrayFrom(chained), array); + // before reset produces empty array as iterator is exhausted + deepEqual(arrayFrom(chained), []); + + var newChain = chained.filter(_.constant(true)); + deepEqual(arrayFrom(newChain), array); + + // original chain is still exhausted + deepEqual(arrayFrom(chained), []); + + newChain = chained.slice(); + deepEqual(arrayFrom(newChain), array); + }); + + test('should work in a lazy sequence' + chainType, 3, function() { + if (true) { + var array = _.range(1, LARGE_ARRAY_SIZE + 1), + values = [], + predicate = function(value) { values.push(value); return value > 99; }, + chained = chain(array); + + deepEqual(arrayFrom(chained), array); + + // resets index & supports lazy array + chained = chained.filter(predicate); + deepEqual(arrayFrom(chained), array.slice(99)); + // Doesn't recompute everything each time + deepEqual(values, array); + } + else { + skipTest(3); + } + }); + }); + + /*--------------------------------------------------------------------------*/ + QUnit.module('lodash(...).plant'); (function() {