From 2747a9224c1418410ca70e0fb85719f5e67aeeff Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Tue, 26 Jun 2012 02:18:44 -0400 Subject: [PATCH] Move `_.groupBy` and `_.sortBy` back to the "Collections" category. [closes #34] --- build.js | 2 +- lodash.js | 209 ++++++++++++++++++++++++--------------------------- test/test.js | 16 ++++ 3 files changed, 115 insertions(+), 112 deletions(-) diff --git a/build.js b/build.js index 52cf7facbd..1f7da51e12 100755 --- a/build.js +++ b/build.js @@ -168,7 +168,7 @@ 'shuffle': [], 'size': ['keys'], 'some': ['identity'], - 'sortBy': [], + 'sortBy': ['map'], 'sortedIndex': ['bind'], 'tap': [], 'template': ['escape'], diff --git a/lodash.js b/lodash.js index a73852b45c..6deafe1520 100644 --- a/lodash.js +++ b/lodash.js @@ -692,6 +692,43 @@ */ var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); + /** + * Splits `collection` into sets, grouped by the result of running each value + * through `callback`. The `callback` is bound to `thisArg` and invoked with 3 + * arguments; (value, index, array). The `callback` argument may also be the + * name of a property to group by. + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to group by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Object} Returns an object of grouped values. + * @example + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); + * // => { '1': [1.3], '2': [2.1, 2.4] } + * + * _.groupBy(['one', 'two', 'three'], 'length'); + * // => { '3': ['one', 'two'], '5': ['three'] } + */ + var groupBy = createIterator(baseIteratorOptions, { + 'init': '{}', + 'top': + 'var prop, isFunc = typeof callback == \'function\';\n' + + 'if (isFunc && thisArg) callback = iteratorBind(callback, thisArg)', + 'inLoop': + 'prop = isFunc\n' + + ' ? callback(collection[index], index, collection)\n' + + ' : collection[index][callback];\n' + + '(hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(collection[index])' + }); + /** * Invokes the method named by `methodName` on each element in the `collection`. * Additional arguments will be passed to each invoked method. If `methodName` @@ -916,6 +953,67 @@ 'inLoop': everyIteratorOptions.inLoop.replace('!', '') }); + + /** + * Produces a new sorted array, ranked in ascending order by the results of + * running each element of `collection` through `callback`. The `callback` is + * bound to `thisArg` and invoked with 3 arguments; (value, index, array). The + * `callback` argument may also be the name of a property to sort by (e.g. 'length'). + * + * @static + * @memberOf _ + * @category Collections + * @param {Array|Object} collection The collection to iterate over. + * @param {Function|String} callback The function called per iteration or + * property name to sort by. + * @param {Mixed} [thisArg] The `this` binding for the callback. + * @returns {Array} Returns a new array of sorted values. + * @example + * + * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); + * // => [3, 1, 2] + * + * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); + * // => [3, 1, 2] + * + * _.sortBy(['larry', 'brendan', 'moe'], 'length'); + * // => ['moe', 'larry', 'brendan'] + */ + function sortBy(collection, callback, thisArg) { + if (typeof callback == 'string') { + var prop = callback; + callback = function(collection) { return collection[prop]; }; + } else if (thisArg) { + callback = iteratorBind(callback, thisArg); + } + + var result = map(collection, function(value, index) { + return { + 'criteria': callback(value, index, collection), + 'value': value + }; + }); + + result.sort(function(left, right) { + var a = left.criteria, + b = right.criteria; + + if (a === undefined) { + return 1; + } + if (b === undefined) { + return -1; + } + return a < b ? -1 : a > b ? 1 : 0; + }); + + var length = result.length; + while (length--) { + result[length] = result[length].value; + } + return result; + } + /** * Converts the `collection`, into an array. Useful for converting the * `arguments` object. @@ -1072,53 +1170,6 @@ return result; } - /** - * Splits `array` into sets, grouped by the result of running each value - * through `callback`. The `callback` is bound to `thisArg` and invoked with 3 - * arguments; (value, index, array). The `callback` argument may also be the - * name of a property to group by. - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to group by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Object} Returns an object of grouped values. - * @example - * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return Math.floor(num); }); - * // => { '1': [1.3], '2': [2.1, 2.4] } - * - * _.groupBy([1.3, 2.1, 2.4], function(num) { return this.floor(num); }, Math); - * // => { '1': [1.3], '2': [2.1, 2.4] } - * - * _.groupBy(['one', 'two', 'three'], 'length'); - * // => { '3': ['one', 'two'], '5': ['three'] } - */ - function groupBy(array, callback, thisArg) { - var result = {}; - if (!array) { - return result; - } - var prop, - value, - index = -1, - isFunc = typeof callback == 'function', - length = array.length; - - if (isFunc && thisArg) { - callback = iteratorBind(callback, thisArg); - } - while (++index < length) { - value = array[index]; - prop = isFunc ? callback(value, index, array) : value[callback]; - (hasOwnProperty.call(result, prop) ? result[prop] : result[prop] = []).push(value); - } - return result; - } - /** * Gets the index at which the first occurrence of `value` is found using * strict equality for comparisons, i.e. `===`. If the `array` is already @@ -1494,70 +1545,6 @@ return result; } - /** - * Produces a new sorted array, ranked in ascending order by the results of - * running each element of `array` through `callback`. The `callback` is - * bound to `thisArg` and invoked with 3 arguments; (value, index, array). The - * `callback` argument may also be the name of a property to sort by (e.g. 'length'). - * - * @static - * @memberOf _ - * @category Arrays - * @param {Array} array The array to iterate over. - * @param {Function|String} callback The function called per iteration or - * property name to sort by. - * @param {Mixed} [thisArg] The `this` binding for the callback. - * @returns {Array} Returns a new array of sorted values. - * @example - * - * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); }); - * // => [3, 1, 2] - * - * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math); - * // => [3, 1, 2] - * - * _.sortBy(['larry', 'brendan', 'moe'], 'length'); - * // => ['moe', 'larry', 'brendan'] - */ - function sortBy(array, callback, thisArg) { - if (!array) { - return []; - } - if (typeof callback == 'string') { - var prop = callback; - callback = function(array) { return array[prop]; }; - } else if (thisArg) { - callback = iteratorBind(callback, thisArg); - } - var index = -1, - length = array.length, - result = Array(length); - - while (++index < length) { - result[index] = { - 'criteria': callback(array[index], index, array), - 'value': array[index] - }; - } - result.sort(function(left, right) { - var a = left.criteria, - b = right.criteria; - - if (a === undefined) { - return 1; - } - if (b === undefined) { - return -1; - } - return a < b ? -1 : a > b ? 1 : 0; - }); - - while (length--) { - result[length] = result[length].value; - } - return result; - } - /** * Uses a binary search to determine the smallest index at which the `value` * should be inserted into `array` in order to maintain the sort order of the diff --git a/test/test.js b/test/test.js index f879363bc7..9b09ab0ffe 100644 --- a/test/test.js +++ b/test/test.js @@ -327,6 +327,14 @@ deepEqual(actual.constructor, [1.3]); deepEqual(actual.hasOwnProperty, [2.1, 2.4]); }); + + test('should work with an object for `collection`', function() { + var actual = _.groupBy({ 'a': 1.3, 'b': 2.1, 'c': 2.4 }, function(num) { + return Math.floor(num); + }); + + deepEqual(actual, { '1': [1.3], '2': [2.1, 2.4] }); + }); }()); /*--------------------------------------------------------------------------*/ @@ -595,6 +603,14 @@ deepEqual(actual, [3, 1, 2]); }); + + test('should work with an object for `collection`', function() { + var actual = _.sortBy({ 'a': 1, 'b': 2, 'c': 3 }, function(num) { + return Math.sin(num); + }); + + deepEqual(actual, [3, 1, 2]); + }); }()); /*--------------------------------------------------------------------------*/