Added thisArg argument to _.sortedIndex and _.uniq, benchmarks, unit tests, and adjusted related dependencies in build.js.

Former-commit-id: a97aa769d760c7cc4d0df6307cebc860345a0da0
This commit is contained in:
John-David Dalton
2012-06-03 21:56:36 -04:00
parent 6ea4226680
commit 9ac64623fc
4 changed files with 193 additions and 47 deletions

View File

@@ -126,14 +126,14 @@
'size': ['keys'], 'size': ['keys'],
'some': ['createIterator', 'identity'], 'some': ['createIterator', 'identity'],
'sortBy': ['map', 'pluck'], 'sortBy': ['map', 'pluck'],
'sortedIndex': [], 'sortedIndex': ['identity'],
'tap': [], 'tap': [],
'template': ['escape'], 'template': ['escape'],
'throttle': [], 'throttle': [],
'times': [], 'times': [],
'toArray': ['values'], 'toArray': ['values'],
'union': ['indexOf'], 'union': ['indexOf'],
'uniq': ['indexOf'], 'uniq': ['identity', 'indexOf'],
'uniqueId': [], 'uniqueId': [],
'values': ['createIterator'], 'values': ['createIterator'],
'without': ['indexOf'], 'without': ['indexOf'],

101
lodash.js
View File

@@ -1072,11 +1072,11 @@
* @returns {Array} Returns a new array of sorted values. * @returns {Array} Returns a new array of sorted values.
* @example * @example
* *
* _.sortBy([1, 2, 3, 4, 5, 6], function(num) { return Math.sin(num); }); * _.sortBy([1, 2, 3], function(num) { return Math.sin(num); });
* // => [5, 4, 6, 3, 1, 2] * // => [3, 1, 2]
* *
* _.sortBy([1, 2, 3, 4, 5, 6], function(num) { return this.sin(num); }, Math); * _.sortBy([1, 2, 3], function(num) { return this.sin(num); }, Math);
* // => [5, 4, 6, 3, 1, 2] * // => [3, 1, 2]
* *
* _.sortBy(['larry', 'brendan', 'moe'], 'length'); * _.sortBy(['larry', 'brendan', 'moe'], 'length');
* // => ['moe', 'larry', 'brendan'] * // => ['moe', 'larry', 'brendan']
@@ -1165,8 +1165,8 @@
* @returns {Array} Returns all but the last value or `n` values of the `array`. * @returns {Array} Returns all but the last value or `n` values of the `array`.
* @example * @example
* *
* _.initial([5, 4, 3, 2, 1]); * _.initial([3, 2, 1]);
* // => [5, 4, 3, 2] * // => [3, 2]
*/ */
function initial(array, n, guard) { function initial(array, n, guard) {
return slice.call(array, 0, -((n == undefined || guard) ? 1 : n)); return slice.call(array, 0, -((n == undefined || guard) ? 1 : n));
@@ -1246,7 +1246,7 @@
* @returns {Array} Returns all but the last value or `n` values of the `array`. * @returns {Array} Returns all but the last value or `n` values of the `array`.
* @example * @example
* *
* _.last([5, 4, 3, 2, 1]); * _.last([3, 2, 1]);
* // => 1 * // => 1
*/ */
function last(array, n, guard) { function last(array, n, guard) {
@@ -1446,8 +1446,8 @@
* @returns {Array} Returns all but the first value or `n` values of the `array`. * @returns {Array} Returns all but the first value or `n` values of the `array`.
* @example * @example
* *
* _.rest([5, 4, 3, 2, 1]); * _.rest([3, 2, 1]);
* // => [4, 3, 2, 1] * // => [2, 1]
*/ */
function rest(array, n, guard) { function rest(array, n, guard) {
return slice.call(array, (n == undefined || guard) ? 1 : n); return slice.call(array, (n == undefined || guard) ? 1 : n);
@@ -1484,37 +1484,53 @@
/** /**
* Uses a binary search to determine the smallest index at which the `value` * Uses a binary search to determine the smallest index at which the `value`
* should be inserted into the `array` in order to maintain the sort order * should be inserted into the `array` in order to maintain the sort order
* of the `array`. If `callback` is passed, it will be executed for `value` and * of the sorted `array`. If `callback` is passed, it will be executed for
* each element in the `array` to compute their sort ranking. The `callback` * `value` and each element in the `array` to compute their sort ranking.
* is invoked with 1 argument; (value). * The `callback` is invoked with 1 argument; (value).
* *
* @static * @static
* @memberOf _ * @memberOf _
* @category Arrays * @category Arrays
* @param {Array} array The array to iterate over. * @param {Array} array The array to iterate over.
* @param {Mixed} value The value to evaluate. * @param {Mixed} value The value to evaluate.
* @param {Function} [callback] The function called per iteration. * @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding for the callback.
* @returns {Number} Returns the index at which the value should be inserted * @returns {Number} Returns the index at which the value should be inserted
* into the array. * into the array.
* @example * @example
* *
* _.sortedIndex([10, 20, 30, 40, 50], 35); * _.sortedIndex([20, 30, 40], 35);
* // => 3 * // => 2
*
* var dict = {
* 'wordToNumber': { 'twenty': 20, 'thirty': 30, 'thirty-five': 35, 'fourty': 40 }
* };
*
* _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) {
* return dict.wordToNumber[word];
* });
* // => 2
*
* _.sortedIndex(['twenty', 'thirty', 'fourty'], 'thirty-five', function(word) {
* return this.wordToNumber[word];
* }, dict);
* // => 2
*/ */
function sortedIndex(array, value, callback) { function sortedIndex(array, value, callback, thisArg) {
var mid, var mid,
low = 0, low = 0,
high = array.length; high = array.length;
if (callback) { if (callback) {
value = callback(value); value = callback.call(thisArg, 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;
if ((callback ? callback(array[mid]) : array[mid]) < value) { }
low = mid + 1; } else {
} else { while (low < high) {
high = mid; mid = (low + high) >>> 1;
array[mid] < value ? low = mid + 1 : high = mid;
} }
} }
return low; return low;
@@ -1562,22 +1578,43 @@
* @category Arrays * @category Arrays
* @param {Array} array The array to process. * @param {Array} array The array to process.
* @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted. * @param {Boolean} [isSorted=false] A flag to indicate that the `array` is already sorted.
* @param {Function} [callback] A * @param {Function} [callback=identity] The function called per iteration.
* @param {Mixed} [thisArg] The `this` binding for the callback.
* @returns {Array} Returns a duplicate-value-free array. * @returns {Array} Returns a duplicate-value-free array.
* @example * @example
* *
* _.uniq([1, 2, 1, 3, 1, 4]); * _.uniq([1, 2, 1, 3, 1]);
* // => [1, 2, 3, 4] * // => [1, 2, 3]
*
* _.uiq([1, 1, 2, 2, 3], true);
* // => [1, 2, 3]
*
* _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return Math.floor(num); });
* // => [1, 2, 3]
*
* _.uniq([1, 2, 1.5, 3, 2.5], function(num) { return this.floor(num); }, Math);
* // => [1, 2, 3]
*/ */
function uniq(array, isSorted, callback) { function uniq(array, isSorted, callback, thisArg) {
var computed, var computed,
index = -1, index = -1,
length = array.length, length = array.length,
result = [], result = [],
seen = []; seen = [];
// juggle arguments
if (typeof isSorted == 'function') {
thisArg = callback;
callback = isSorted;
isSorted = false;
}
if (!callback) {
callback = identity;
} else if (thisArg) {
callback = iteratorBind(callback, thisArg);
}
while (++index < length) { while (++index < length) {
computed = callback ? callback(array[index]) : array[index]; computed = callback(array[index], index, array);
if (isSorted if (isSorted
? !index || seen[seen.length - 1] !== computed ? !index || seen[seen.length - 1] !== computed
: indexOf(seen, computed) < 0 : indexOf(seen, computed) < 0
@@ -2134,8 +2171,8 @@
* @example * @example
* *
* var iceCream = { 'flavor': 'chocolate' }; * var iceCream = { 'flavor': 'chocolate' };
* _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'lots' }); * _.defaults(iceCream, { 'flavor': 'vanilla', 'sprinkles': 'rainbow' });
* // => { 'flavor': 'chocolate', 'sprinkles': 'lots' } * // => { 'flavor': 'chocolate', 'sprinkles': 'rainbow' }
*/ */
var defaults = createIterator(extendIteratorOptions, { var defaults = createIterator(extendIteratorOptions, {
'inLoop': 'if (object[index] == undefined)' + extendIteratorOptions.inLoop 'inLoop': 'if (object[index] == undefined)' + extendIteratorOptions.inLoop
@@ -2219,7 +2256,7 @@
var isArguments = function(value) { var isArguments = function(value) {
return toString.call(value) == '[object Arguments]'; return toString.call(value) == '[object Arguments]';
}; };
// fallback for browser like IE<9 which detect `arguments` as `[object Object]` // fallback for browser like IE < 9 which detect `arguments` as `[object Object]`
if (!isArguments(arguments)) { if (!isArguments(arguments)) {
isArguments = function(value) { isArguments = function(value) {
return !!(value && hasOwnProperty.call(value, 'callee')); return !!(value && hasOwnProperty.call(value, 'callee'));

View File

@@ -55,7 +55,7 @@
function log(text) { function log(text) {
console.log(text); console.log(text);
if (fbPanel) { if (fbPanel) {
// scroll down the Firebug Lite panel // scroll the Firebug Lite panel down
fbPanel.scrollTop = fbPanel.scrollHeight; fbPanel.scrollTop = fbPanel.scrollHeight;
} }
} }
@@ -74,12 +74,7 @@
object = {}, object = {},
fourNumbers = [5, 25, 10, 30], fourNumbers = [5, 25, 10, 30],
nestedNumbers = [1, [2], [3, [[4]]]], nestedNumbers = [1, [2], [3, [[4]]]],
twoNumbers = [12, 21], twoNumbers = [12, 21];
words = [
'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine',
'ten', 'eleven', 'twelve', 'thirteen', 'fourteen', 'fifteen', 'sixteen',
'seventeen', 'eighteen', 'nineteen', 'twenty'
];
var ctor = function() { }, var ctor = function() { },
func = function(greeting) { return greeting + ': ' + this.name; }; func = function(greeting) { return greeting + ': ' + this.name; };
@@ -92,6 +87,36 @@
_boundCtor = _.bind(ctor, { 'name': 'moe' }), _boundCtor = _.bind(ctor, { 'name': 'moe' }),
_boundPartial = _.bind(func, { 'name': 'moe' }, 'hi'); _boundPartial = _.bind(func, { 'name': 'moe' }, 'hi');
var wordToNumber = {
'one': 1,
'two': 2,
'three': 3,
'four': 4,
'five': 5,
'six': 6,
'seven': 7,
'eight': 8,
'nine': 9,
'ten': 10,
'eleven': 11,
'twelve': 12,
'thirteen': 13,
'fourteen': 14,
'fifteen': 15,
'sixteen': 16,
'seventeen': 17,
'eighteen': 18,
'nineteen': 19,
'twenty': 20,
'twenty-one': 21,
'twenty-two': 22,
'twenty-three': 23,
'twenty-four': 24,
'twenty-five': 25
};
var words = _.keys(wordToNumber).slice(0, length);
for (var index = 0; index < length; index++) { for (var index = 0; index < length; index++) {
numbers[index] = index; numbers[index] = index;
object['key' + index] = index; object['key' + index] = index;
@@ -486,6 +511,32 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
suites.push(
Benchmark.Suite('sortedIndex')
.add('Lo-Dash', function() {
lodash.sortedIndex(numbers, 25);
})
.add('Underscore', function() {
_.sortedIndex(numbers, 25);
})
);
suites.push(
Benchmark.Suite('sortedIndex callback')
.add('Lo-Dash', function() {
lodash.sortedIndex(words, 'twenty-five', function(value) {
return wordToNumber[value];
});
})
.add('Underscore', function() {
_.sortedIndex(words, 'twenty-five', function(value) {
return wordToNumber[value];
});
})
);
/*--------------------------------------------------------------------------*/
suites.push( suites.push(
Benchmark.Suite('times') Benchmark.Suite('times')
.add('Lo-Dash', function() { .add('Lo-Dash', function() {
@@ -524,6 +575,32 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
suites.push(
Benchmark.Suite('uniq')
.add('Lo-Dash', function() {
lodash.uniq(numbers.concat(fourNumbers, twoNumbers));
})
.add('Underscore', function() {
_.uniq(numbers.concat(fourNumbers, twoNumbers));
})
);
suites.push(
Benchmark.Suite('uniq callback')
.add('Lo-Dash', function() {
lodash.uniq(numbers.concat(fourNumbers, twoNumbers), function(num) {
return num % 2;
});
})
.add('Underscore', function() {
_.uniq(numbers.concat(fourNumbers, twoNumbers), function(num) {
return num % 2;
});
})
);
/*--------------------------------------------------------------------------*/
suites.push( suites.push(
Benchmark.Suite('values') Benchmark.Suite('values')
.add('Lo-Dash', function() { .add('Lo-Dash', function() {

View File

@@ -142,6 +142,10 @@
test('should not escape the ">" character', function() { test('should not escape the ">" character', function() {
equal(_.escape('>'), '>'); equal(_.escape('>'), '>');
}); });
test('should not escape the "/" character', function() {
equal(_.escape('/'), '/');
});
}()); }());
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -177,14 +181,14 @@
QUnit.module('lodash.find'); QUnit.module('lodash.find');
(function() { (function() {
var array = [1, 2, 3, 4]; var array = [1, 2, 3];
test('should return found `value`', function() { test('should return found `value`', function() {
equal(_.find(array, function(n) { return n > 2; }), 3); equal(_.find(array, function(n) { return n > 2; }), 3);
}); });
test('should return `undefined` if `value` is not found', function() { test('should return `undefined` if `value` is not found', function() {
equal(_.find(array, function(n) { return n == 5; }), undefined); equal(_.find(array, function(n) { return n == 4; }), undefined);
}); });
}()); }());
@@ -215,7 +219,7 @@
(function() { (function() {
test('returns the collection', function() { test('returns the collection', function() {
var collection = [1, 2, 3, 4]; var collection = [1, 2, 3];
equal(_.forEach(collection, Boolean), collection); equal(_.forEach(collection, Boolean), collection);
}); });
@@ -294,7 +298,7 @@
(function() { (function() {
test('returns an empty collection for `n` of `0`', function() { test('returns an empty collection for `n` of `0`', function() {
var array = [1, 2, 3, 4]; var array = [1, 2, 3];
deepEqual(_.initial(array, 0), []); deepEqual(_.initial(array, 0), []);
}); });
}()); }());
@@ -481,11 +485,25 @@
(function() { (function() {
test('supports the `thisArg` argument', function() { test('supports the `thisArg` argument', function() {
var actual = _.sortBy([1, 2, 3, 4], function(num) { var actual = _.sortBy([1, 2, 3], function(num) {
return this.sin(num); return this.sin(num);
}, Math); }, Math);
deepEqual(actual, [4, 3, 1, 2]); deepEqual(actual, [3, 1, 2]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.sortedIndex');
(function() {
test('supports the `thisArg` argument', function() {
var actual = _.sortedIndex([1, 2, 3], 4, function(num) {
return this.sin(num);
}, Math);
equal(actual, 0);
}); });
}()); }());
@@ -555,6 +573,20 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('lodash.uniq');
(function() {
test('supports the `thisArg` argument', function() {
var actual = _.uniq([1, 2, 1.5, 3, 2.5], function(num) {
return this.floor(num);
}, Math);
deepEqual(actual, [1, 2, 3]);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash(...).shift'); QUnit.module('lodash(...).shift');
(function() { (function() {