Cleanup _.isEqual and make _.where of an empty array match all arrays similar to empty objects matching all objects.

This commit is contained in:
John-David Dalton
2014-07-02 09:37:40 -07:00
parent 61eeaa0496
commit 9ba1ddc2dd
2 changed files with 87 additions and 93 deletions

116
lodash.js
View File

@@ -1926,38 +1926,18 @@
if (valClass != othClass) { if (valClass != othClass) {
return false; return false;
} }
switch (valClass) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal
return +value == +other;
case numberClass:
// treat `NaN` vs. `NaN` as equal
return (value != +value)
? other != +other
// but treat `-0` vs. `+0` as not equal
: (value == 0 ? (1 / value == 1 / other) : value == +other);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4) and
// treat strings primitives and string objects as equal
return value == String(other);
}
var isArr = arrayLikeClasses[valClass], var isArr = arrayLikeClasses[valClass],
isErr = valClass == errorClass; isErr = valClass == errorClass;
if (!support.argsClass) { if (isArr) {
valIsArg = isArguments(value); var valLength = value.length,
othIsArg = isArguments(other); othLength = other.length;
}
if (!isArr) { if (valLength != othLength && !(isWhere && othLength > valLength)) {
// exit for things like functions and DOM nodes
if (!(isErr || valClass == objectClass) || (!support.nodeClass && (isNode(value) || isNode(other)))) {
return false; return false;
} }
}
else if (isErr || (valClass == objectClass && (support.nodeClass || !(isNode(value) || isNode(other))))) {
// unwrap any `lodash` wrapped values // unwrap any `lodash` wrapped values
var valWrapped = hasOwnProperty.call(value, '__wrapped__'), var valWrapped = hasOwnProperty.call(value, '__wrapped__'),
othWrapped = hasOwnProperty.call(other, '__wrapped__'); othWrapped = hasOwnProperty.call(other, '__wrapped__');
@@ -1965,6 +1945,10 @@
if (valWrapped || othWrapped) { if (valWrapped || othWrapped) {
return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, customizer, isWhere, stackA, stackB); return baseIsEqual(valWrapped ? value.__wrapped__ : value, othWrapped ? other.__wrapped__ : other, customizer, isWhere, stackA, stackB);
} }
if (!support.argsClass) {
valIsArg = isArguments(value);
othIsArg = isArguments(other);
}
var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'), var hasValCtor = !valIsArg && hasOwnProperty.call(value, 'constructor'),
hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor'); hasOthCtor = !othIsArg && hasOwnProperty.call(other, 'constructor');
@@ -1988,6 +1972,43 @@
return false; return false;
} }
} }
var valProps = isErr ? ['message', 'name'] : keys(value),
othProps = isErr ? valProps : keys(other);
if (valIsArg) {
valProps.push('length');
}
if (othIsArg) {
othProps.push('length');
}
valLength = valProps.length;
othLength = othProps.length;
if (valLength != othLength && !isWhere) {
return false;
}
}
else {
switch (valClass) {
case boolClass:
case dateClass:
// coerce dates and booleans to numbers, dates to milliseconds and booleans
// to `1` or `0` treating invalid dates coerced to `NaN` as not equal
return +value == +other;
case numberClass:
// treat `NaN` vs. `NaN` as equal
return (value != +value)
? other != +other
// but treat `-0` vs. `+0` as not equal
: (value == 0 ? (1 / value == 1 / other) : value == +other);
case regexpClass:
case stringClass:
// coerce regexes to strings (http://es5.github.io/#x15.10.6.4) and
// treat strings primitives and string objects as equal
return value == String(other);
}
return false;
} }
// assume cyclic structures are equal // assume cyclic structures are equal
// the algorithm for detecting cyclic structures is adapted from ES 5.1 // the algorithm for detecting cyclic structures is adapted from ES 5.1
@@ -1995,27 +2016,21 @@
stackA || (stackA = []); stackA || (stackA = []);
stackB || (stackB = []); stackB || (stackB = []);
var length = stackA.length; var index = stackA.length;
while (length--) { while (index--) {
if (stackA[length] == value) { if (stackA[index] == value) {
return stackB[length] == other; return stackB[index] == other;
} }
} }
var index = -1;
// add `value` and `other` to the stack of traversed objects // add `value` and `other` to the stack of traversed objects
stackA.push(value); stackA.push(value);
stackB.push(other); stackB.push(other);
// recursively compare objects and arrays (susceptible to call stack limits) // recursively compare objects and arrays (susceptible to call stack limits)
result = true;
if (isArr) { if (isArr) {
var othLength = other.length;
length = value.length;
result = length == othLength;
if (result || (isWhere && othLength > length)) {
// deep compare the contents, ignoring non-numeric properties // deep compare the contents, ignoring non-numeric properties
while (++index < length) { while (result && ++index < valLength) {
var valValue = value[index]; var valValue = value[index];
if (isWhere) { if (isWhere) {
var othIndex = othLength; var othIndex = othLength;
@@ -2032,27 +2047,10 @@
result = baseIsEqual(valValue, othValue, customizer, isWhere, stackA, stackB); result = baseIsEqual(valValue, othValue, customizer, isWhere, stackA, stackB);
} }
} }
if (!result) {
break;
}
}
} }
} }
else { else {
var valProps = isErr ? ['message', 'name'] : keys(value), while (result && ++index < valLength) {
othProps = isErr ? valProps : keys(other);
if (valIsArg) {
valProps.push('length');
}
if (othIsArg) {
othProps.push('length');
}
length = valProps.length;
result = length == othProps.length;
if (result || isWhere) {
while (++index < length) {
var key = valProps[index]; var key = valProps[index];
result = isErr || hasOwnProperty.call(other, key); result = isErr || hasOwnProperty.call(other, key);
@@ -2064,10 +2062,6 @@
result = baseIsEqual(valValue, othValue, customizer, isWhere, stackA, stackB); result = baseIsEqual(valValue, othValue, customizer, isWhere, stackA, stackB);
} }
} }
if (!result) {
break;
}
}
} }
} }
stackA.pop(); stackA.pop();

View File

@@ -10663,7 +10663,7 @@
deepEqual(actual, [collection[1]]); deepEqual(actual, [collection[1]]);
actual = _.where(collection, { 'a': [] }); actual = _.where(collection, { 'a': [] });
deepEqual(actual, []); deepEqual(actual, collection);
actual = _.where(collection, { 'a': ['b', 'd'] }); actual = _.where(collection, { 'a': ['b', 'd'] });
deepEqual(actual, []); deepEqual(actual, []);