Loosen _.matches to match objects with inherited properties. [closes #1067]

This commit is contained in:
jdalton
2015-03-21 23:33:19 -07:00
parent 8930e6b393
commit f20d8f5cc0
2 changed files with 40 additions and 34 deletions

View File

@@ -2265,12 +2265,12 @@
* @param {*} value The value to compare.
* @param {*} other The other value to compare.
* @param {Function} [customizer] The function to customize comparing values.
* @param {boolean} [isWhere] Specify performing partial comparisons.
* @param {boolean} [isLoose] Specify performing partial comparisons.
* @param {Array} [stackA] Tracks traversed `value` objects.
* @param {Array} [stackB] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the values are equivalent, else `false`.
*/
function baseIsEqual(value, other, customizer, isWhere, stackA, stackB) {
function baseIsEqual(value, other, customizer, isLoose, stackA, stackB) {
// Exit early for identical values.
if (value === other) {
// Treat `+0` vs. `-0` as not equal.
@@ -2285,7 +2285,7 @@
// Return `false` unless both values are `NaN`.
return value !== value && other !== other;
}
return baseIsEqualDeep(value, other, baseIsEqual, customizer, isWhere, stackA, stackB);
return baseIsEqualDeep(value, other, baseIsEqual, customizer, isLoose, stackA, stackB);
}
/**
@@ -2298,12 +2298,12 @@
* @param {Object} other The other object to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparing objects.
* @param {boolean} [isWhere] Specify performing partial comparisons.
* @param {boolean} [isLoose] Specify performing partial comparisons.
* @param {Array} [stackA=[]] Tracks traversed `value` objects.
* @param {Array} [stackB=[]] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
function baseIsEqualDeep(object, other, equalFunc, customizer, isWhere, stackA, stackB) {
function baseIsEqualDeep(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
var objIsArr = isArray(object),
othIsArr = isArray(other),
objTag = arrayTag,
@@ -2325,21 +2325,27 @@
othIsArr = isTypedArray(other);
}
}
var objIsObj = objTag == objectTag && !isHostObject(object),
var objIsObj = (objTag == objectTag || (isLoose && objTag == funcTag)) && !isHostObject(object),
othIsObj = othTag == objectTag && !isHostObject(other),
isSameTag = objTag == othTag;
if (isSameTag && !(objIsArr || objIsObj)) {
return equalByTag(object, other, objTag);
}
var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
if (isLoose) {
if (!isSameTag && !(objIsObj && othIsObj)) {
return false;
}
} else {
var valWrapped = objIsObj && hasOwnProperty.call(object, '__wrapped__'),
othWrapped = othIsObj && hasOwnProperty.call(other, '__wrapped__');
if (valWrapped || othWrapped) {
return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isWhere, stackA, stackB);
}
if (!isSameTag) {
return false;
if (valWrapped || othWrapped) {
return equalFunc(valWrapped ? object.value() : object, othWrapped ? other.value() : other, customizer, isLoose, stackA, stackB);
}
if (!isSameTag) {
return false;
}
}
// Assume cyclic values are equal.
// For more information on detecting circular references see https://es5.github.io/#JO.
@@ -2356,7 +2362,7 @@
stackA.push(object);
stackB.push(other);
var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isWhere, stackA, stackB);
var result = (objIsArr ? equalArrays : equalObjects)(object, other, equalFunc, customizer, isLoose, stackA, stackB);
stackA.pop();
stackB.pop();
@@ -2387,7 +2393,7 @@
while (++index < length) {
if ((noCustomizer && strictCompareFlags[index])
? values[index] !== object[props[index]]
: !hasOwnProperty.call(object, props[index])
: !(props[index] in object)
) {
return false;
}
@@ -2396,7 +2402,7 @@
while (++index < length) {
var key = props[index];
if (noCustomizer && strictCompareFlags[index]) {
var result = hasOwnProperty.call(object, key);
var result = key in object;
} else {
var objValue = object[key],
srcValue = values[index];
@@ -2447,7 +2453,7 @@
if (isStrictComparable(value)) {
return function(object) {
return object != null && object[key] === value && hasOwnProperty.call(object, key);
return object != null && object[key] === value && key in object;
};
}
}
@@ -2476,7 +2482,7 @@
function baseMatchesProperty(key, value) {
if (isStrictComparable(value)) {
return function(object) {
return object != null && object[key] === value;
return object != null && object[key] === value && key in object;
};
}
return function(object) {
@@ -3771,18 +3777,18 @@
* @param {Array} other The other array to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparing arrays.
* @param {boolean} [isWhere] Specify performing partial comparisons.
* @param {boolean} [isLoose] Specify performing partial comparisons.
* @param {Array} [stackA] Tracks traversed `value` objects.
* @param {Array} [stackB] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
*/
function equalArrays(array, other, equalFunc, customizer, isWhere, stackA, stackB) {
function equalArrays(array, other, equalFunc, customizer, isLoose, stackA, stackB) {
var index = -1,
arrLength = array.length,
othLength = other.length,
result = true;
if (arrLength != othLength && !(isWhere && othLength > arrLength)) {
if (arrLength != othLength && !(isLoose && othLength > arrLength)) {
return false;
}
// Deep compare the contents, ignoring non-numeric properties.
@@ -3792,23 +3798,23 @@
result = undefined;
if (customizer) {
result = isWhere
result = isLoose
? customizer(othValue, arrValue, index)
: customizer(arrValue, othValue, index);
}
if (typeof result == 'undefined') {
// Recursively compare arrays (susceptible to call stack limits).
if (isWhere) {
if (isLoose) {
var othIndex = othLength;
while (othIndex--) {
othValue = other[othIndex];
result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB);
result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
if (result) {
break;
}
}
} else {
result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isWhere, stackA, stackB);
result = (arrValue && arrValue === othValue) || equalFunc(arrValue, othValue, customizer, isLoose, stackA, stackB);
}
}
}
@@ -3864,18 +3870,18 @@
* @param {Object} other The other object to compare.
* @param {Function} equalFunc The function to determine equivalents of values.
* @param {Function} [customizer] The function to customize comparing values.
* @param {boolean} [isWhere] Specify performing partial comparisons.
* @param {boolean} [isLoose] Specify performing partial comparisons.
* @param {Array} [stackA] Tracks traversed `value` objects.
* @param {Array} [stackB] Tracks traversed `other` objects.
* @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
*/
function equalObjects(object, other, equalFunc, customizer, isWhere, stackA, stackB) {
function equalObjects(object, other, equalFunc, customizer, isLoose, stackA, stackB) {
var objProps = keys(object),
objLength = objProps.length,
othProps = keys(other),
othLength = othProps.length;
if (objLength != othLength && !isWhere) {
if (objLength != othLength && !isLoose) {
return false;
}
var hasCtor,
@@ -3883,7 +3889,7 @@
while (++index < objLength) {
var key = objProps[index],
result = hasOwnProperty.call(other, key);
result = isLoose ? key in other : hasOwnProperty.call(other, key);
if (result) {
var objValue = object[key],
@@ -3891,13 +3897,13 @@
result = undefined;
if (customizer) {
result = isWhere
result = isLoose
? customizer(othValue, objValue, key)
: customizer(objValue, othValue, key);
}
if (typeof result == 'undefined') {
// Recursively compare objects (susceptible to call stack limits).
result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isWhere, stackA, stackB);
result = (objValue && objValue === othValue) || equalFunc(objValue, othValue, customizer, isLoose, stackA, stackB);
}
}
if (!result) {
@@ -3905,7 +3911,7 @@
}
hasCtor || (hasCtor = key == 'constructor');
}
if (!hasCtor) {
if (!hasCtor && !isLoose) {
var objCtor = object.constructor,
othCtor = other.constructor;
@@ -8715,7 +8721,7 @@
value = source[key];
if (isStrictComparable(value)) {
return object != null && value === object[key] && hasOwnProperty.call(object, key);
return object != null && value === object[key] && key in object;
}
}
var values = Array(length),

View File

@@ -9634,7 +9634,7 @@
objects = [{ 'a': 1 }, { 'a': 1, 'b': 1 }, { 'a': 1, 'b': undefined }],
actual = _.map(objects, matches);
deepEqual(actual, [true, false, true]);
deepEqual(actual, [false, false, true]);
matches = _.matchesProperty('a', { 'b': undefined });
objects = [{ 'a': { 'a': 1 } }, { 'a': { 'a': 1, 'b': 1 } }, { 'a': { 'a': 1, 'b': undefined } }];