diff --git a/build.js b/build.js index 88cf5201c..f2fc9ba1d 100644 --- a/build.js +++ b/build.js @@ -115,12 +115,18 @@ 'filter': ['baseEach', 'createCallback', 'isArray'], 'find': ['baseEach', 'createCallback', 'isArray'], 'findIndex': ['createCallback'], + 'findLastIndex': ['createCallback'], 'findKey': ['createCallback', 'forOwn'], + 'findLast': ['createCallback', 'forEachRight'], + 'findLastKey': ['createCallback', 'forOwnRight'], 'first': ['createCallback', 'slice'], 'flatten': ['baseFlatten', 'map'], 'forEach': ['baseEach', 'createCallback', 'isArray'], + 'forEachRight': ['createCallback', 'forEach', 'isString', 'keys'], 'forIn': ['createIterator'], + 'forInRight': ['createCallback', 'forIn'], 'forOwn': ['createIterator'], + 'forOwnRight': ['createCallback', 'keys'], 'functions': ['forIn', 'isFunction'], 'groupBy': ['createAggregator'], 'has': [], @@ -169,7 +175,7 @@ 'random': [], 'range': [], 'reduce': ['baseEach', 'createCallback', 'isArray'], - 'reduceRight': ['createCallback', 'forEach', 'isString', 'keys'], + 'reduceRight': ['createCallback', 'forEachRight'], 'reject': ['createCallback', 'filter'], 'rest': ['createCallback', 'slice'], 'result': ['isFunction'], @@ -295,6 +301,7 @@ 'compact', 'difference', 'findIndex', + 'findLastIndex', 'first', 'flatten', 'indexOf', @@ -323,8 +330,10 @@ 'every', 'filter', 'find', + 'findLast', 'findWhere', 'forEach', + 'forEachRight', 'groupBy', 'indexBy', 'invoke', @@ -365,8 +374,11 @@ 'cloneDeep', 'defaults', 'findKey', + 'findLastKey', 'forIn', + 'forInRight', 'forOwn', + 'forOwnRight', 'functions', 'has', 'invert', @@ -515,8 +527,14 @@ 'createCallback', 'findIndex', 'findKey', + 'findLast', + 'findLastIndex', + 'findLastKey', + 'forEachRight', 'forIn', + 'forInRight', 'forOwn', + 'forOwnRight', 'indexBy', 'isPlainObject', 'merge', diff --git a/build/pre-compile.js b/build/pre-compile.js index 34fc80fc9..d81b20f72 100644 --- a/build/pre-compile.js +++ b/build/pre-compile.js @@ -133,14 +133,20 @@ 'find', 'findIndex', 'findKey', + 'findLast', + 'findLastIndex', + 'findLastKey', 'findWhere', 'first', 'flatten', 'foldl', 'foldr', 'forEach', + 'forEachRight', 'forIn', + 'forInRight', 'forOwn', + 'forOwnRight', 'function', 'functions', 'global', diff --git a/lodash.js b/lodash.js index d326d0296..09374e969 100644 --- a/lodash.js +++ b/lodash.js @@ -1931,7 +1931,7 @@ var defaults = createIterator(defaultsIteratorOptions); /** - * This method is similar to `_.find`, except that it returns the key of the + * This method is like `_.findIndex`, except that it returns the key of the * element that passes the callback check, instead of the element itself. * * @static @@ -1948,7 +1948,7 @@ * _.findKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) { * return num % 2 == 0; * }); - * // => 'b' + * // => 'b' (order is not guaranteed) */ function findKey(object, callback, thisArg) { var result; @@ -1962,6 +1962,38 @@ return result; } + /** + * This method is like `_.findKey`, except that it iterates over elements + * of a `collection` in the opposite order. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to search. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the key of the found element, else `undefined`. + * @example + * + * _.findLastKey({ 'a': 1, 'b': 2, 'c': 3, 'd': 4 }, function(num) { + * return num % 2 == 1; + * }); + * // => returns `c`, assuming `_.findKey` returns `a` + */ + function findLastKey(object, callback, thisArg) { + var result; + callback = lodash.createCallback(callback, thisArg); + forOwnRight(object, function(value, key, object) { + if (callback(value, key, object)) { + result = key; + return false; + } + }); + return result; + } + /** * Iterates over own and inherited enumerable properties of a given `object`, * executing the `callback` for each property. The `callback` is bound to @@ -1983,18 +2015,62 @@ * } * * Dog.prototype.bark = function() { - * alert('Woof, woof!'); + * console.log('Woof, woof!'); * }; * * _.forIn(new Dog('Dagny'), function(value, key) { - * alert(key); + * console.log(key); * }); - * // => alerts 'name' and 'bark' (order is not guaranteed) + * // => logs 'bark' and 'name' (order is not guaranteed) */ var forIn = createIterator(eachIteratorOptions, forOwnIteratorOptions, { 'useHas': false }); + /** + * This method is like `_.forIn`, except that it iterates over elements + * of a `collection` in the opposite order. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * function Dog(name) { + * this.name = name; + * } + * + * Dog.prototype.bark = function() { + * console.log('Woof, woof!'); + * }; + * + * _.forInRight(new Dog('Dagny'), function(value, key) { + * console.log(key); + * }); + * // => logs 'name' and 'bark' assuming `_.forIn ` logs 'bark' and 'name' + */ + function forInRight(object, callback, thisArg) { + var index = -1, + pairs = []; + + forIn(object, function(value, key) { + pairs.push(value, key); + }); + + var length = pairs.length; + callback = lodash.createCallback(callback, thisArg, 3); + while (++index < length) { + if (callback(pairs[index], pairs[++index], object) === false) { + break; + } + } + return object; + } + /** * Iterates over own enumerable properties of a given `object`, executing the * `callback` for each property. The `callback` is bound to `thisArg` and @@ -2012,12 +2088,44 @@ * @example * * _.forOwn({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { - * alert(key); + * console.log(key); * }); - * // => alerts '0', '1', and 'length' (order is not guaranteed) + * // => logs '0', '1', and 'length' (order is not guaranteed) */ var forOwn = createIterator(eachIteratorOptions, forOwnIteratorOptions); + /** + * This method is like `_.forOwn`, except that it iterates over elements + * of a `collection` in the opposite order. + * + * @static + * @memberOf _ + * @category Objects + * @param {Object} object The object to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Object} Returns `object`. + * @example + * + * _.forOwnRight({ '0': 'zero', '1': 'one', 'length': 2 }, function(num, key) { + * console.log(key); + * }); + * // => logs 'length', '1', and '0' assuming `_.forOwn` logs '0', '1', and 'length' + */ + function forOwnRight(object, callback, thisArg) { + var props = keys(object), + length = props.length; + + callback = lodash.createCallback(callback, thisArg, 3); + while (length--) { + var key = props[length]; + if (callback(object[key], key, object) === false) { + break; + } + } + return object; + } + /** * Creates a sorted array of property names of all enumerable properties, * own and inherited, of `object` that have function values. @@ -3061,6 +3169,38 @@ } } + /** + * This method is like `_.find`, except that it iterates over elements + * of a `collection` from right to left. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the found element, else `undefined`. + * @example + * + * _.findLast([1, 2, 3, 4], function(num) { + * return num % 2 == 1; + * }); + * // => 3 + */ + function findLast(collection, callback, thisArg) { + var result; + callback = lodash.createCallback(callback, thisArg); + forEachRight(collection, function(value, index, collection) { + if (callback(value, index, collection)) { + result = value; + return false; + } + }); + return result; + } + /** * Iterates over elements of a `collection`, executing the `callback` for * each element. The `callback` is bound to `thisArg` and invoked with three @@ -3077,11 +3217,11 @@ * @returns {Array|Object|String} Returns `collection`. * @example * - * _([1, 2, 3]).forEach(alert).join(','); - * // => alerts each number and returns '1,2,3' + * _([1, 2, 3]).forEach(function(num) { console.log(num); }).join(','); + * // => logs each number and returns '1,2,3' * - * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, alert); - * // => alerts each number value (order is not guaranteed) + * _.forEach({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { console.log(num); }); + * // => logs each number value and returns the object (order is not guaranteed) */ function forEach(collection, callback, thisArg) { if (callback && typeof thisArg == 'undefined' && isArray(collection)) { @@ -3099,6 +3239,41 @@ return collection; } + /** + * This method is like `_.forEach`, except that it iterates over elements + * of a `collection` from right to left. + * + * @static + * @memberOf _ + * @alias each + * @category Collections + * @param {Array|Object|String} collection The collection to iterate over. + * @param {Function} [callback=identity] The function called per iteration. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Array|Object|String} Returns `collection`. + * @example + * + * _([1, 2, 3]).forEachRight(function(num) { console.log(num); }).join(','); + * // => logs each number from right to left and returns '3,2,1' + */ + function forEachRight(collection, callback, thisArg) { + var iterable = collection, + length = collection ? collection.length : 0; + + if (typeof length != 'number') { + var props = keys(collection); + length = props.length; + } else if (support.unindexedChars && isString(collection)) { + iterable = collection.split(''); + } + callback = lodash.createCallback(callback, thisArg, 3); + forEach(collection, function(value, index, collection) { + index = props ? props[--length] : --length; + callback(iterable[index], index, collection); + }); + return collection; + } + /** * Creates an object composed of keys generated from the results of running * each element of the `collection` through the `callback`. The corresponding @@ -3488,7 +3663,7 @@ } /** - * This method is similar to `_.reduce`, except that it iterates over elements + * This method is like `_.reduce`, except that it iterates over elements * of a `collection` from right to left. * * @static @@ -3507,22 +3682,12 @@ * // => [4, 5, 2, 3, 0, 1] */ function reduceRight(collection, callback, accumulator, thisArg) { - var iterable = collection, - length = collection ? collection.length : 0, - noaccum = arguments.length < 3; - - if (typeof length != 'number') { - var props = keys(collection); - length = props.length; - } else if (support.unindexedChars && isString(collection)) { - iterable = collection.split(''); - } + var noaccum = arguments.length < 3; callback = lodash.createCallback(callback, thisArg, 4); - forEach(collection, function(value, index, collection) { - index = props ? props[--length] : --length; + forEachRight(collection, function(value, index, collection) { accumulator = noaccum - ? (noaccum = false, iterable[index]) - : callback(accumulator, iterable[index], index, collection); + ? (noaccum = false, value) + : callback(accumulator, value, index, collection); }); return accumulator; } @@ -3871,8 +4036,8 @@ } /** - * This method is similar to `_.find`, except that it returns the index of - * the element that passes the callback check, instead of the element itself. + * This method is like `_.find`, except that it returns the index of the + * element that passes the callback check, instead of the element itself. * * @static * @memberOf _ @@ -3903,6 +4068,39 @@ return -1; } + /** + * This method is like `_.findIndex`, except that it iterates over elements + * of a `collection` from right to left. + * + * @static + * @memberOf _ + * @category Arrays + * @param {Array} array The array to search. + * @param {Function|Object|String} [callback=identity] The function called per + * iteration. If a property name or object is passed, it will be used to create + * a "_.pluck" or "_.where" style callback, respectively. + * @param {Mixed} [thisArg] The `this` binding of `callback`. + * @returns {Mixed} Returns the index of the found element, else `-1`. + * @example + * + * _.findLastIndex(['apple', 'banana', 'beet'], function(food) { + * return /^b/.test(food); + * }); + * // => 2 + */ + function findLastIndex(array, callback, thisArg) { + var index = -1, + length = array ? array.length : 0; + + callback = lodash.createCallback(callback, thisArg); + while (length--) { + if (callback(array[index], index, array)) { + return index; + } + } + return -1; + } + /** * Gets the first element of the `array`. If a number `n` is passed, the first * `n` elements of the `array` are returned. If a `callback` function is passed, @@ -4731,12 +4929,12 @@ * * var view = { * 'label': 'docs', - * 'onClick': function() { alert('clicked ' + this.label); } + * 'onClick': function() { console.log('clicked ' + this.label); } * }; * * _.bindAll(view); * jQuery('#docs').on('click', view.onClick); - * // => alerts 'clicked docs', when the button is clicked + * // => logs 'clicked docs', when the button is clicked */ function bindAll(object) { var funcs = arguments.length > 1 ? baseFlatten(arguments, true, false, 1) : functions(object), @@ -5067,8 +5265,8 @@ * @returns {Number} Returns the timer id. * @example * - * _.defer(function() { alert('deferred'); }); - * // returns from the function before `alert` is called + * _.defer(function() { console.log('deferred'); }); + * // returns from the function before 'deferred' is logged */ function defer(func) { var args = nativeSlice.call(arguments, 1); @@ -5191,7 +5389,7 @@ } /** - * This method is similar to `_.partial`, except that `partial` arguments are + * This method is like `_.partial`, except that `partial` arguments are * appended to those passed to the new function. * * @static @@ -5772,10 +5970,10 @@ * * _([1, 2, 3, 4]) * .filter(function(num) { return num % 2 == 0; }) - * .tap(alert) + * .tap(function(array) { console.log(array); }) * .map(function(num) { return num * num; }) * .value(); - * // => // [2, 4] (alerted) + * // => // [2, 4] (logged) * // => [4, 16] */ function tap(value, interceptor) { @@ -5837,8 +6035,11 @@ lodash.filter = filter; lodash.flatten = flatten; lodash.forEach = forEach; + lodash.forEachRight = forEachRight; lodash.forIn = forIn; + lodash.forInRight = forInRight; lodash.forOwn = forOwn; + lodash.forOwnRight = forOwnRight; lodash.functions = functions; lodash.groupBy = groupBy; lodash.indexBy = indexBy; @@ -5907,7 +6108,10 @@ lodash.every = every; lodash.find = find; lodash.findIndex = findIndex; + lodash.findLast = findLast; + lodash.findLastIndex = findLastIndex; lodash.findKey = findKey; + lodash.findLastKey = findLastKey; lodash.has = has; lodash.identity = identity; lodash.indexOf = indexOf; diff --git a/test/test-build.js b/test/test-build.js index ed0dedb0e..2448768c8 100644 --- a/test/test-build.js +++ b/test/test-build.js @@ -97,6 +97,7 @@ 'compact', 'difference', 'findIndex', + 'findLastIndex', 'first', 'flatten', 'indexOf', @@ -125,8 +126,10 @@ 'every', 'filter', 'find', + 'findLast', 'findWhere', 'forEach', + 'forEachRight', 'groupBy', 'indexBy', 'invoke', @@ -167,8 +170,11 @@ 'cloneDeep', 'defaults', 'findKey', + 'findLastKey', 'forIn', + 'forInRight', 'forOwn', + 'forOwnRight', 'functions', 'has', 'invert', @@ -279,8 +285,14 @@ 'createCallback', 'findIndex', 'findKey', + 'findLast', + 'findLastIndex', + 'findLastKey', + 'forEachRight', 'forIn', + 'forInRight', 'forOwn', + 'forOwnRight', 'indexBy', 'isPlainObject', 'merge',