Move _.pluck and _.invoke back to the "Collections" category and optimize _.sortedIndex when a callback is passed.

Former-commit-id: d16763e7d35660d8ba9ea976c8b2a4dc20f1211f
This commit is contained in:
John-David Dalton
2012-06-14 00:28:36 -04:00
parent e5555dd26a
commit 24c9b6e211
3 changed files with 112 additions and 89 deletions

View File

@@ -8,6 +8,7 @@
/** Used to minify variables embedded in compiled strings */ /** Used to minify variables embedded in compiled strings */
var compiledVars = [ var compiledVars = [
'accumulator', 'accumulator',
'args',
'arrayClass', 'arrayClass',
'callback', 'callback',
'className', 'className',
@@ -18,14 +19,17 @@
'hasOwnProperty', 'hasOwnProperty',
'identity', 'identity',
'index', 'index',
'isFunc',
'iteratorBind', 'iteratorBind',
'length', 'length',
'methodName',
'noaccum',
'object', 'object',
'objectTypes', 'objectTypes',
'noaccum',
'property', 'property',
'result', 'result',
'skipProto', 'skipProto',
'slice',
'source', 'source',
'sourceIndex', 'sourceIndex',
'stringClass', 'stringClass',

169
lodash.js
View File

@@ -339,6 +339,20 @@
} }
}; };
/** Reusable iterator options for `invoke`, `map`, and `pluck` */
var mapIteratorOptions = {
'init': '',
'exit': 'if (!collection) return []',
'beforeLoop': {
'array': 'result = Array(length)',
'object': 'result = []'
},
'inLoop': {
'array': 'result[index] = callback(collection[index], index, collection)',
'object': 'result.push(callback(collection[index], index, collection))'
}
};
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
/** /**
@@ -437,13 +451,13 @@
// create the function factory // create the function factory
var factory = Function( var factory = Function(
'arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, ' + 'arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, ' +
'stringClass, toString, undefined', 'slice, stringClass, toString, undefined',
'"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}' '"use strict"; return function(' + args + ') {\n' + iteratorTemplate(data) + '\n}'
); );
// return the compiled function // return the compiled function
return factory( return factory(
arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes, arrayClass, funcClass, hasOwnProperty, identity, iteratorBind, objectTypes,
stringClass, toString slice, stringClass, toString
); );
} }
@@ -679,7 +693,40 @@
var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions); var forEach = createIterator(baseIteratorOptions, forEachIteratorOptions);
/** /**
* Produces a new array of values by mapping each value in the `collection` * Invokes the method named by `methodName` on each element in the `collection`.
* Additional arguments will be passed to each invoked method. If `methodName`
* is a function it will be invoked for, and `this` bound to, each element
* in the `collection`.
*
* @static
* @memberOf _
* @category Collections
* @param {Array|Object} collection The collection to iterate over.
* @param {Function|String} methodName The name of the method to invoke or
* the function invoked per iteration.
* @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
* @returns {Array} Returns a new array of values returned from each invoked method.
* @example
*
* _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
* // => [[1, 5, 7], [1, 2, 3]]
*
* _.invoke([123, 456], String.prototype.split, '');
* // => [['1', '2', '3'], ['4', '5', '6']]
*/
var invoke = createIterator(mapIteratorOptions, {
'args': 'collection, methodName',
'top':
'var args = slice.call(arguments, 2),\n' +
' isFunc = typeof methodName == \'function\'',
'inLoop': {
'array': 'result[index] = (isFunc ? methodName : collection[index][methodName]).apply(collection[index], args)',
'object': 'result.push((isFunc ? methodName : collection[index][methodName]).apply(collection[index], args))'
}
});
/**
* Produces a new array of values by mapping each element in the `collection`
* through a transformation `callback`. The `callback` is bound to `thisArg` * through a transformation `callback`. The `callback` is bound to `thisArg`
* and invoked with 3 arguments; for arrays they are (value, index, array) * and invoked with 3 arguments; for arrays they are (value, index, array)
* and for objects they are (value, key, object). * and for objects they are (value, key, object).
@@ -700,16 +747,34 @@
* _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; }); * _.map({ 'one': 1, 'two': 2, 'three': 3 }, function(num) { return num * 3; });
* // => [3, 6, 9] (order is not guaranteed) * // => [3, 6, 9] (order is not guaranteed)
*/ */
var map = createIterator(baseIteratorOptions, { var map = createIterator(baseIteratorOptions, mapIteratorOptions);
'init': '',
'exit': 'if (!collection) return []', /**
'beforeLoop': { * Retrieves the value of a specified property from all elements in
'array': 'result = Array(length)', * the `collection`.
'object': 'result = []' *
}, * @static
* @memberOf _
* @category Collections
* @param {Array|Object} collection The collection to iterate over.
* @param {String} property The property to pluck.
* @returns {Array} Returns a new array of property values.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 },
* { 'name': 'curly', 'age': 60 }
* ];
*
* _.pluck(stooges, 'name');
* // => ['moe', 'larry', 'curly']
*/
var pluck = createIterator(mapIteratorOptions, {
'args': 'collection, property',
'inLoop': { 'inLoop': {
'array': 'result[index] = callback(collection[index], index, collection)', 'array': 'result[index] = collection[index][property]',
'object': 'result.push(callback(collection[index], index, collection))' 'object': 'result.push(collection[index][property])'
} }
}); });
@@ -1159,44 +1224,6 @@
return result; return result;
} }
/**
* Invokes the method named by `methodName` on each element of `array`.
* Additional arguments will be passed to each invoked method. If `methodName`
* is a function it will be invoked for, and `this` bound to, each element
* of `array`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to iterate over.
* @param {Function|String} methodName The name of the method to invoke or
* the function invoked per iteration.
* @param {Mixed} [arg1, arg2, ...] Arguments to invoke the method with.
* @returns {Array} Returns a new array of values returned from each invoked method.
* @example
*
* _.invoke([[5, 1, 7], [3, 2, 1]], 'sort');
* // => [[1, 5, 7], [1, 2, 3]]
*
* _.invoke([123, 456], String.prototype.split, '');
* // => [['1', '2', '3'], ['4', '5', '6']]
*/
function invoke(array, methodName) {
var result = [];
if (!array) {
return result;
}
var args = slice.call(arguments, 2),
index = -1,
length = array.length,
isFunc = typeof methodName == 'function';
while (++index < length) {
result[index] = (isFunc ? methodName : array[index][methodName]).apply(array[index], args);
}
return result;
}
/** /**
* Gets the last value of the `array`. Pass `n` to return the lasy `n` values * Gets the last value of the `array`. Pass `n` to return the lasy `n` values
* of the `array`. * of the `array`.
@@ -1363,40 +1390,6 @@
return result; return result;
} }
/**
* Retrieves the value of a specified property from all elements in `array`.
*
* @static
* @memberOf _
* @category Arrays
* @param {Array} array The array to iterate over.
* @param {String} property The property to pluck.
* @returns {Array} Returns a new array of property values.
* @example
*
* var stooges = [
* { 'name': 'moe', 'age': 40 },
* { 'name': 'larry', 'age': 50 },
* { 'name': 'curly', 'age': 60 }
* ];
*
* _.pluck(stooges, 'name');
* // => ['moe', 'larry', 'curly']
*/
function pluck(array, property) {
if (!array) {
return [];
}
var index = -1,
length = array.length,
result = Array(length);
while (++index < length) {
result[index] = array[index][property];
}
return result;
}
/** /**
* Creates an array of numbers (positive and/or negative) progressing from * Creates an array of numbers (positive and/or negative) progressing from
* `start` up to but not including `stop`. This method is a port of Python's * `start` up to but not including `stop`. This method is a port of Python's
@@ -1608,10 +1601,14 @@
high = array.length; high = array.length;
if (callback) { if (callback) {
value = callback.call(thisArg, value); if (thisArg) {
var fn = callback;
callback = function(value) { return fn.call(thisArg, value); };
}
value = callback(value);
while (low < high) { while (low < high) {
mid = (low + high) >>> 1; mid = (low + high) >>> 1;
callback.call(thisArg, array[mid]) < value ? low = mid + 1 : high = mid; callback(array[mid]) < value ? low = mid + 1 : high = mid;
} }
} else { } else {
while (low < high) { while (low < high) {

View File

@@ -378,6 +378,17 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('lodash.invoke');
(function() {
test('should work with an object for `collection`', function() {
var object = { 'a': 1, 'b': 2, 'c': 3 };
deepEqual(_.invoke(object, 'toFixed', 1), ['1.0', '2.0', '3.0']);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.isEmpty'); QUnit.module('lodash.isEmpty');
(function() { (function() {
@@ -520,6 +531,17 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('lodash.pluck');
(function() {
test('should work with an object for `collection`', function() {
var object = { 'a': [1], 'b': [1, 2], 'c': [1, 2, 3] };
deepEqual(_.pluck(object, 'length'), [1, 2, 3]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.reduceRight'); QUnit.module('lodash.reduceRight');
(function() { (function() {
@@ -743,12 +765,10 @@
'indexOf', 'indexOf',
'initial', 'initial',
'intersection', 'intersection',
'invoke',
'last', 'last',
'lastIndexOf', 'lastIndexOf',
'max', 'max',
'min', 'min',
'pluck',
'range', 'range',
'rest', 'rest',
'shuffle', 'shuffle',
@@ -782,7 +802,9 @@
'filter', 'filter',
'find', 'find',
'forEach', 'forEach',
'invoke',
'map', 'map',
'pluck',
'reduce', 'reduce',
'reduceRight', 'reduceRight',
'reject', 'reject',