Ensure "Arrays" and "Objects" methods work with arguments objects and arrays respectively.

Former-commit-id: aebb7a0004d804b7fd43d73e24d1da28c67f4059
This commit is contained in:
John-David Dalton
2013-04-07 15:10:03 -07:00
parent 93df901b71
commit 43037c0ff9
3 changed files with 100 additions and 32 deletions

View File

@@ -168,7 +168,7 @@
'times': ['createCallback'], 'times': ['createCallback'],
'toArray': ['isString', 'values'], 'toArray': ['isString', 'values'],
'unescape': [], 'unescape': [],
'union': ['uniq'], 'union': ['isArray', 'uniq'],
'uniq': ['createCallback', 'indexOf'], 'uniq': ['createCallback', 'indexOf'],
'uniqueId': [], 'uniqueId': [],
'unzip': ['max', 'pluck'], 'unzip': ['max', 'pluck'],
@@ -2030,12 +2030,12 @@
'function difference(array) {', 'function difference(array) {',
' var index = -1,', ' var index = -1,',
' length = array.length,', ' length = array.length,',
' flattened = concat.apply(arrayRef, arguments),', ' flattened = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),',
' result = [];', ' result = [];',
'', '',
' while (++index < length) {', ' while (++index < length) {',
' var value = array[index];', ' var value = array[index];',
' if (indexOf(flattened, value, length) < 0) {', ' if (indexOf(flattened, value) < 0) {',
' result.push(value);', ' result.push(value);',
' }', ' }',
' }', ' }',
@@ -2219,11 +2219,11 @@
// replace `_.omit` // replace `_.omit`
source = replaceFunction(source, 'omit', [ source = replaceFunction(source, 'omit', [
'function omit(object) {', 'function omit(object) {',
' var props = concat.apply(arrayRef, arguments),', ' var props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),',
' result = {};', ' result = {};',
'', '',
' forIn(object, function(value, key) {', ' forIn(object, function(value, key) {',
' if (indexOf(props, key, 1) < 0) {', ' if (indexOf(props, key) < 0) {',
' result[key] = value;', ' result[key] = value;',
' }', ' }',
' });', ' });',
@@ -2234,8 +2234,8 @@
// replace `_.pick` // replace `_.pick`
source = replaceFunction(source, 'pick', [ source = replaceFunction(source, 'pick', [
'function pick(object) {', 'function pick(object) {',
' var index = 0,', ' var index = -1,',
' props = concat.apply(arrayRef, arguments),', ' props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),',
' length = props.length,', ' length = props.length,',
' result = {};', ' result = {};',
'', '',

View File

@@ -580,16 +580,15 @@
* @private * @private
* @param {Array} array The array to search. * @param {Array} array The array to search.
* @param {Mixed} value The value to search for. * @param {Mixed} value The value to search for.
* @param {Number} fromIndex The index to search from.
* @returns {Boolean} Returns `true`, if `value` is found, else `false`. * @returns {Boolean} Returns `true`, if `value` is found, else `false`.
*/ */
function cachedContains(array, fromIndex) { function cachedContains(array) {
var length = array.length, var length = array.length,
isLarge = (length - fromIndex) >= largeArraySize; isLarge = length >= largeArraySize;
if (isLarge) { if (isLarge) {
var cache = {}, var cache = {},
index = fromIndex - 1; index = -1;
while (++index < length) { while (++index < length) {
var key = keyPrefix + array[index]; var key = keyPrefix + array[index];
@@ -601,7 +600,7 @@
var key = keyPrefix + value; var key = keyPrefix + value;
return cache[key] && indexOf(cache[key], value) > -1; return cache[key] && indexOf(cache[key], value) > -1;
} }
return indexOf(array, value, fromIndex) > -1; return indexOf(array, value) > -1;
} }
} }
@@ -2111,12 +2110,12 @@
if (isFunc) { if (isFunc) {
callback = lodash.createCallback(callback, thisArg); callback = lodash.createCallback(callback, thisArg);
} else { } else {
var props = concat.apply(arrayRef, arguments); var props = concat.apply(arrayRef, nativeSlice.call(arguments, 1));
} }
forIn(object, function(value, key, object) { forIn(object, function(value, key, object) {
if (isFunc if (isFunc
? !callback(value, key, object) ? !callback(value, key, object)
: indexOf(props, key, 1) < 0 : indexOf(props, key) < 0
) { ) {
result[key] = value; result[key] = value;
} }
@@ -2179,8 +2178,8 @@
function pick(object, callback, thisArg) { function pick(object, callback, thisArg) {
var result = {}; var result = {};
if (typeof callback != 'function') { if (typeof callback != 'function') {
var index = 0, var index = -1,
props = concat.apply(arrayRef, arguments), props = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
length = isObject(object) ? props.length : 0; length = isObject(object) ? props.length : 0;
while (++index < length) { while (++index < length) {
@@ -3292,8 +3291,8 @@
function difference(array) { function difference(array) {
var index = -1, var index = -1,
length = array ? array.length : 0, length = array ? array.length : 0,
flattened = concat.apply(arrayRef, arguments), flattened = concat.apply(arrayRef, nativeSlice.call(arguments, 1)),
contains = cachedContains(flattened, length), contains = cachedContains(flattened),
result = []; result = [];
while (++index < length) { while (++index < length) {
@@ -3643,7 +3642,7 @@
} }
var argsIndex = argsLength; var argsIndex = argsLength;
while (--argsIndex) { while (--argsIndex) {
if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex], 0)))(value)) { if (!(cache[argsIndex] || (cache[argsIndex] = cachedContains(args[argsIndex])))(value)) {
continue outer; continue outer;
} }
} }
@@ -3967,8 +3966,11 @@
* _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); * _.union([1, 2, 3], [101, 2, 1, 10], [2, 1]);
* // => [1, 2, 3, 101, 10] * // => [1, 2, 3, 101, 10]
*/ */
function union() { function union(array) {
return uniq(concat.apply(arrayRef, arguments)); return uniq(isArray(array)
? concat.apply(arrayRef, arguments)
: concat.apply(array ? nativeSlice.call(array) : arrayRef, nativeSlice.call(arguments, 1))
);
} }
/** /**
@@ -4258,8 +4260,8 @@
* // => alerts 'clicked docs', when the button is clicked * // => alerts 'clicked docs', when the button is clicked
*/ */
function bindAll(object) { function bindAll(object) {
var funcs = concat.apply(arrayRef, arguments), var funcs = arguments.length > 1 ? concat.apply(arrayRef, nativeSlice.call(arguments, 1)) : functions(object),
index = funcs.length > 1 ? 0 : (funcs = functions(object), -1), index = -1,
length = funcs.length; length = funcs.length;
while (++index < length) { while (++index < length) {

View File

@@ -196,6 +196,8 @@
QUnit.module('lodash.at'); QUnit.module('lodash.at');
(function() { (function() {
var args = arguments;
test('should return `undefined` for nonexistent keys', function() { test('should return `undefined` for nonexistent keys', function() {
var actual = _.at(['a', 'b', 'c'], [0, 2, 4]); var actual = _.at(['a', 'b', 'c'], [0, 2, 4]);
deepEqual(actual, ['a', 'c', undefined]); deepEqual(actual, ['a', 'c', undefined]);
@@ -210,6 +212,11 @@
deepEqual(actual, ['a', 'c', 'd']); deepEqual(actual, ['a', 'c', 'd']);
}); });
test('should work with an `arguments` object for `collection`', function() {
var actual = _.at(args, [0, 2]);
deepEqual(actual, ['a', 'c']);
});
test('should work with an object for `collection`', function() { test('should work with an object for `collection`', function() {
var actual = _.at({ 'a': 1, 'b': 2, 'c': 3 }, ['a', 'c']); var actual = _.at({ 'a': 1, 'b': 2, 'c': 3 }, ['a', 'c']);
deepEqual(actual, [1, 3]); deepEqual(actual, [1, 3]);
@@ -224,7 +231,7 @@
deepEqual(_.at(collection, [0, 2]), ['a', 'c']); deepEqual(_.at(collection, [0, 2]), ['a', 'c']);
}); });
}); });
}()); }('a', 'b', 'c'));
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -295,6 +302,12 @@
deepEqual(actual, [1, 2, 3, undefined]); deepEqual(actual, [1, 2, 3, undefined]);
}); });
test('should work with an array `object` argument', function() {
var array = ['push', 'pop'];
_.bindAll(array);
equal(array.pop, Array.prototype.pop);
});
}()); }());
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
@@ -334,7 +347,7 @@
}; };
var objects = { var objects = {
'an arguments object': arguments, 'an `arguments` object': arguments,
'an array': ['a', 'b', 'c', ''], 'an array': ['a', 'b', 'c', ''],
'an array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 }, 'an array-like-object': { '0': 'a', '1': 'b', '2': 'c', '3': '', 'length': 5 },
'boolean': false, 'boolean': false,
@@ -1900,6 +1913,10 @@
deepEqual(_.omit(new Foo, 'a'), expected); deepEqual(_.omit(new Foo, 'a'), expected);
}); });
test('should work with an array `object` argument', function() {
deepEqual(_.omit([1, 2, 3], '0', '2'), { '1': 2 });
});
test('should work with a `callback` argument', function() { test('should work with a `callback` argument', function() {
var actual = _.omit(object, function(value) { var actual = _.omit(object, function(value) {
return value == 1; return value == 1;
@@ -2024,6 +2041,10 @@
deepEqual(_.pick(new Foo, 'b'), { 'b': 2 }); deepEqual(_.pick(new Foo, 'b'), { 'b': 2 });
}); });
test('should work with an array `object` argument', function() {
deepEqual(_.pick([1, 2, 3], '1'), { '1': 2 });
});
test('should work with a `callback` argument', function() { test('should work with a `callback` argument', function() {
var actual = _.pick(object, function(value) { var actual = _.pick(object, function(value) {
return value == 2; return value == 2;
@@ -2855,6 +2876,19 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('lodash.union');
(function() {
test('should produce correct results when passed a falsey `array` argument', function() {
var expected = [1, 2, 3],
actual = _.union(null, expected);
deepEqual(actual, expected);
});
}());
/*--------------------------------------------------------------------------*/
QUnit.module('lodash.uniq'); QUnit.module('lodash.uniq');
(function() { (function() {
@@ -2867,10 +2901,14 @@
}); });
test('should distinguish between numbers and numeric strings', function() { test('should distinguish between numbers and numeric strings', function() {
var expected = ['2', 2, Object('2'), Object(2)], var array = [],
actual = _.uniq(expected); expected = ['2', 2, Object('2'), Object(2)];
deepEqual(actual, expected); _.times(50, function() {
array.push.apply(array, expected);
});
deepEqual(_.uniq(expected), expected);
}); });
_.each({ _.each({
@@ -3167,6 +3205,37 @@
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/
QUnit.module('"Arrays" methods');
(function() {
var args = arguments;
test('should work with `arguments` objects', function() {
function message(methodName) {
return '_.' + methodName + ' should work with `arguments` objects';
}
deepEqual(_.compact(args), [1, [3], 5], message('compact'));
deepEqual(_.difference(args, [null]), [1, [3], 5], message('difference'));
deepEqual(_.findIndex(args, _.identity), 0, message('findIndex'));
deepEqual(_.first(args), 1, message('first'));
deepEqual(_.flatten(args), [1, null, 3, null, 5], message('flatten'));
deepEqual(_.indexOf(args, 5), 4, message('indexOf'));
deepEqual(_.initial(args, 4), [1], message('initial'));
deepEqual(_.intersection(args, [1]), [1], message('intersection'));
deepEqual(_.last(args), 5, message('last'));
deepEqual(_.lastIndexOf(args, 1), 0, message('lastIndexOf'));
deepEqual(_.rest(args, 4), [5], message('rest'));
deepEqual(_.sortedIndex(args, 6), 5, message('sortedIndex'));
deepEqual(_.union(args, [null, 6]), [1, null, [3], 5, 6], message('union'));
deepEqual(_.uniq(args), [1, null, [3], 5], message('uniq'));
deepEqual(_.without(args, null), [1, [3], 5], message('without'));
deepEqual(_.zip(args, args), [[1, 1], [null, null], [[3], [3]], [null, null], [5, 5]], message('zip'));
});
}(1, null, [3], null, 5));
/*--------------------------------------------------------------------------*/
QUnit.module('lodash methods'); QUnit.module('lodash methods');
(function() { (function() {
@@ -3227,13 +3296,10 @@
_.each(funcs, function(methodName) { _.each(funcs, function(methodName) {
var actual = [], var actual = [],
expected = _.map(falsey, function() { return []; }),
func = _[methodName], func = _[methodName],
pass = true; pass = true;
var expected = (methodName == 'union')
? _.map(falsey, function(value, index) { return index ? [value] : []; })
: _.map(falsey, function() { return []; });
_.each(falsey, function(value, index) { _.each(falsey, function(value, index) {
try { try {
actual.push(index ? func(value) : func()); actual.push(index ? func(value) : func());