Loosen restrictions of _.intersection and others to accept array-like objects and add isArrayLike helper. [closes #1163]

This commit is contained in:
jdalton
2015-04-28 09:18:32 -07:00
parent bf89287c56
commit 2c6d880542
2 changed files with 42 additions and 49 deletions

View File

@@ -1829,8 +1829,8 @@
*/
function baseAt(collection, props) {
var index = -1,
isArr = isArrayLike(collection),
length = collection.length,
isArr = isLength(length),
propsLength = props.length,
result = Array(propsLength);
@@ -2169,8 +2169,8 @@
*
* @private
* @param {Array} array The array to flatten.
* @param {boolean} isDeep Specify a deep flatten.
* @param {boolean} isStrict Restrict flattening to arrays and `arguments` objects.
* @param {boolean} [isDeep] Specify a deep flatten.
* @param {boolean} [isStrict] Restrict flattening to arrays-like objects.
* @returns {Array} Returns the new flattened array.
*/
function baseFlatten(array, isDeep, isStrict) {
@@ -2181,8 +2181,8 @@
while (++index < length) {
var value = array[index];
if (isObjectLike(value) && isLength(value.length) && (isArray(value) || isArguments(value))) {
if (isObjectLike(value) && isArrayLike(value) &&
(isStrict || isArray(value) || isArguments(value))) {
if (isDeep) {
// Recursively flatten arrays (susceptible to call stack limits).
value = baseFlatten(value, isDeep, isStrict);
@@ -2483,8 +2483,7 @@
*/
function baseMap(collection, iteratee) {
var index = -1,
length = getLength(collection),
result = isLength(length) ? Array(length) : [];
result = isArrayLike(collection) ? Array(collection.length) : [];
baseEach(collection, function(value, key, collection) {
result[++index] = iteratee(value, key, collection);
@@ -2584,7 +2583,7 @@
if (!isObject(object)) {
return object;
}
var isSrcArr = isLength(source.length) && (isArray(source) || isTypedArray(source));
var isSrcArr = isArrayLike(source) && (isArray(source) || isTypedArray(source));
if (!isSrcArr) {
var props = keys(source);
push.apply(props, getSymbols(source));
@@ -2647,10 +2646,10 @@
if (isCommon) {
result = srcValue;
if (isLength(srcValue.length) && (isArray(srcValue) || isTypedArray(srcValue))) {
if (isArrayLike(srcValue) && (isArray(srcValue) || isTypedArray(srcValue))) {
result = isArray(value)
? value
: (getLength(value) ? arrayCopy(value) : []);
: (isArrayLike(value) ? arrayCopy(value) : []);
}
else if (isPlainObject(srcValue) || isArguments(srcValue)) {
result = isArguments(value)
@@ -3292,11 +3291,11 @@
*/
function createBaseEach(eachFunc, fromRight) {
return function(collection, iteratee) {
var length = collection ? getLength(collection) : 0;
if (!isLength(length)) {
if (!isArrayLike(collection)) {
return eachFunc(collection, iteratee);
}
var index = fromRight ? length : -1,
var length = collection.length,
index = fromRight ? length : -1,
iterable = toObject(collection);
while ((fromRight ? index-- : ++index < length)) {
@@ -4309,6 +4308,17 @@
return func == null ? undefined : func.apply(object, args);
}
/**
* Checks if `value` is array-like.
*
* @private
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is array-like, else `false`.
*/
function isArrayLike(value) {
return value != null && isLength(getLength(value));
}
/**
* Checks if `value` is a valid array-like index.
*
@@ -4337,13 +4347,9 @@
return false;
}
var type = typeof index;
if (type == 'number') {
var length = getLength(object),
prereq = isLength(length) && isIndex(index, length);
} else {
prereq = type == 'string' && index in object;
}
if (prereq) {
if (type == 'number'
? (isArrayLike(object) && isIndex(index, object.length))
: (type == 'string' && index in object)) {
var other = object[index];
return value === value ? (value === other) : (other !== other);
}
@@ -4657,7 +4663,7 @@
if (value == null) {
return [];
}
if (!isLength(getLength(value))) {
if (!isArrayLike(value)) {
return values(value);
}
if (lodash.support.unindexedChars && isString(value)) {
@@ -4806,7 +4812,7 @@
* // => [1, 3]
*/
var difference = restParam(function(array, values) {
return (isArray(array) || isArguments(array))
return isArrayLike(array)
? baseDifference(array, baseFlatten(values, false, true))
: [];
});
@@ -5294,7 +5300,7 @@
while (++argsIndex < argsLength) {
var value = arguments[argsIndex];
if (isArray(value) || isArguments(value)) {
if (isArrayLike(value)) {
args.push(value);
caches.push((isCommon && value.length >= 120) ? createCache(argsIndex && value) : null);
}
@@ -5954,7 +5960,7 @@
length = 0;
array = arrayFilter(array, function(group) {
if (isArray(group) || isArguments(group)) {
if (isArrayLike(group)) {
length = nativeMax(group.length, length);
return true;
}
@@ -6021,7 +6027,7 @@
* // => [3]
*/
var without = restParam(function(array, values) {
return (isArray(array) || isArguments(array))
return isArrayLike(array)
? baseDifference(array, values)
: [];
});
@@ -6046,7 +6052,7 @@
while (++index < length) {
var array = arguments[index];
if (isArray(array) || isArguments(array)) {
if (isArrayLike(array)) {
var result = result
? baseDifference(result, array).concat(baseDifference(array, result))
: array;
@@ -6419,8 +6425,7 @@
* // => ['barney', 'pebbles']
*/
var at = restParam(function(collection, props) {
var length = collection ? getLength(collection) : 0;
if (isLength(length)) {
if (isArrayLike(collection)) {
collection = toIterable(collection);
}
return baseAt(collection, baseFlatten(props));
@@ -6921,8 +6926,7 @@
var index = -1,
isFunc = typeof path == 'function',
isProp = isKey(path),
length = getLength(collection),
result = isLength(length) ? Array(length) : [];
result = isArrayLike(collection) ? Array(collection.length) : [];
baseEach(collection, function(value) {
var func = isFunc ? path : (isProp && value != null && value[path]);
@@ -8661,15 +8665,13 @@
* // => false
*/
function isArguments(value) {
var length = isObjectLike(value) ? value.length : undefined;
return isLength(length) && objToString.call(value) == argsTag;
return isObjectLike(value) && isArrayLike(value) && objToString.call(value) == argsTag;
}
// Fallback for environments without a `toStringTag` for `arguments` objects.
if (!support.argsTag) {
isArguments = function(value) {
var length = isObjectLike(value) ? value.length : undefined;
return isLength(length) && hasOwnProperty.call(value, 'callee') &&
!propertyIsEnumerable.call(value, 'callee');
return isObjectLike(value) && isArrayLike(value) &&
hasOwnProperty.call(value, 'callee') && !propertyIsEnumerable.call(value, 'callee');
};
}
@@ -8791,10 +8793,9 @@
if (value == null) {
return true;
}
var length = getLength(value);
if (isLength(length) && (isArray(value) || isString(value) || isArguments(value) ||
if (isArrayLike(value) && (isArray(value) || isString(value) || isArguments(value) ||
(isObjectLike(value) && isFunction(value.splice)))) {
return !length;
return !value.length;
}
return !keys(value).length;
}
@@ -9785,12 +9786,9 @@
* // => ['0', '1']
*/
var keys = !nativeKeys ? shimKeys : function(object) {
if (object) {
var Ctor = object.constructor,
length = object.length;
}
var Ctor = object != null && object.constructor;
if ((typeof Ctor == 'function' && Ctor.prototype === object) ||
(typeof object == 'function' ? lodash.support.enumPrototypes : isLength(length))) {
(typeof object == 'function' ? lodash.support.enumPrototypes : isArrayLike(object))) {
return shimKeys(object);
}
return isObject(object) ? nativeKeys(object) : [];

View File

@@ -3855,12 +3855,11 @@
deepEqual(_.difference([1, NaN, 3], largeArray), [1, 3]);
});
test('should ignore values that are not arrays or `arguments` objects', 4, function() {
test('should ignore values that are not array-like', 3, function() {
var array = [1, null, 3];
deepEqual(_.difference(args, 3, { '0': 1 }), [1, 2, 3]);
deepEqual(_.difference(null, array, 1), []);
deepEqual(_.difference(array, args, null), [null]);
deepEqual(_.difference('abc', array, 'b'), []);
});
}(1, 2, 3));
@@ -16532,10 +16531,6 @@
var array = [1, 2, 3, 1, 2, 3];
deepEqual(_.without(array, 1, 2), [3, 3]);
});
test('should treat string values for `array` as empty', 1, function() {
deepEqual(_.without('abc', 'b'), []);
});
}(1, 2, 3));
/*--------------------------------------------------------------------------*/