diff --git a/lodash.js b/lodash.js index 7ed194fb5..0244c7279 100644 --- a/lodash.js +++ b/lodash.js @@ -842,8 +842,9 @@ * * The wrapper methods that support shortcut fusion are: * `compact`, `drop`, `dropRight`, `dropRightWhile`, `dropWhile`, `filter`, - * `first`, `initial`, `last`, `map`, `reject`, `rest`, `reverse`, `slice`, - * `take`, `takeRight`, `takeRightWhile`, `takeWhile`, and `toArray` + * `find`, `findLast`, `first`, `initial`, `last`, `map`, `reject`, `rest`, + * `reverse`, `slice`, `take`, `takeRight`, `takeRightWhile`, `takeWhile`, + * and `toArray` * * The chainable wrapper methods are: * `after`, `ary`, `assign`, `assignWith`, `at`, `before`, `bind`, `bindAll`, @@ -11582,6 +11583,14 @@ return this.filter(identity); }; + LazyWrapper.prototype.find = function(predicate) { + return this.filter(predicate).first(); + }; + + LazyWrapper.prototype.findLast = function(predicate) { + return this.reverse().find(predicate); + }; + LazyWrapper.prototype.reject = function(predicate) { predicate = getIteratee(predicate); return this.filter(function(value) { @@ -11619,14 +11628,15 @@ // Add `LazyWrapper` methods to `lodash.prototype`. baseForOwn(LazyWrapper.prototype, function(func, methodName) { var checkIteratee = /^(?:filter|map|reject)|While$/.test(methodName), - retUnwrapped = /^(?:first|last)$/.test(methodName), - lodashFunc = lodash[retUnwrapped ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName]; + isTaker = /^(?:first|last)$/.test(methodName), + retUnwrapped = isTaker || /^find/.test(methodName), + lodashFunc = lodash[isTaker ? ('take' + (methodName == 'last' ? 'Right' : '')) : methodName]; if (!lodashFunc) { return; } lodash.prototype[methodName] = function() { - var args = retUnwrapped ? [1] : arguments, + var args = isTaker ? [1] : arguments, chainAll = this.__chain__, value = this.__wrapped__, isHybrid = !!this.__actions__.length, @@ -11640,7 +11650,7 @@ } var interceptor = function(value) { var result = lodashFunc.apply(lodash, arrayPush([value], args)); - return (retUnwrapped && chainAll) ? result[0] : result; + return (isTaker && chainAll) ? result[0] : result; }; var action = { 'func': thru, 'args': [interceptor], 'thisArg': undefined }, @@ -11653,7 +11663,7 @@ return func.apply(this, args); } var result = lodashFunc.apply(lodash, arrayPush([this.value()], args)); - return result[0]; + return isTaker ? result[0] : result; } if (!retUnwrapped && useLazy) { value = onlyLazy ? value : new LazyWrapper(this); diff --git a/test/test.js b/test/test.js index 5e508a273..47faf419b 100644 --- a/test/test.js +++ b/test/test.js @@ -3977,6 +3977,44 @@ deepEqual(actual, expecting); }); + + if (!/(Index|Key)/.test(methodName)) { + test('should enable shortcut fusion in chaining', 2, function() { + if (!isNpm) { + var callCount = 0; + var items = _.range(0, LARGE_ARRAY_SIZE + 1); + var expectedCalls = LARGE_ARRAY_SIZE / 2 + 1; + var expected = LARGE_ARRAY_SIZE / 2 - 25; + var result = _(items).map(function(x) { + callCount++; + return x - 25; + })[methodName](function(x) { + return x === expected; + }); + strictEqual(callCount, expectedCalls); + strictEqual(result, expected); + } else { + skipTest(2); + } + }); + + test('should function when the only action in chaining', 2, function() { + if (!isNpm) { + var callCount = 0; + var items = _.range(0, LARGE_ARRAY_SIZE + 1); + var expectedCalls = LARGE_ARRAY_SIZE / 2 + 1; + var expected = LARGE_ARRAY_SIZE / 2; + var result = _(items)[methodName](function(x) { + callCount++; + return x === expected; + }); + strictEqual(callCount, expectedCalls); + strictEqual(result, expected); + } else { + skipTest(2); + } + }); + } }()); (function() {