Much ink has been spilled on the subject of testing two objects for deep equality in JavaScript. None, however, seem to care about distinguishing the following two objects:
var o1 = [{},{}];
var subitem = {};
var o2 = [subitem, subitem];
var o3 = [{}, {}];
Most deep equality algorithms would say that o1
, o2
and o3
are equal. I want an algorithm that says that o1
and o2
are not equal, but o1
and o3
are equal. To put it differently, I want an algorithm that tells me if the pointer graphs have the same structure or not. I care about this because if I have an a modification of the first element is reflected in the second in o2
, but not in o1
.
This means that deep equality of cyclic structures should work:
var o1 = [];
o1.push(o1);
var o2 = [];
o2.push(o2);
// deepGraphEqual(o1, o2) == true
var o3 = [[]];
o3[0].push(o3);
// deepGraphEqual(o1, o3) == false
If you're going to avoid mutating the items, you are probably going to need ECMAScript6 maps, so I'll accept solutions that use those.
deepEqual() method tests if two objects, and their child objects, are equal, using the == operator. If the two objects are not equal, an assertion failure is being caused, and the program is terminated. To compare the objects using the === operator, use the assert. deepStrictEqual() method.
If the two objects have the same values, equals() will return true . In the second comparison, equals() checks to see whether the passed object is null, or if it's typed as a different class. If it's a different class then the objects are not equal. Finally, equals() compares the objects' fields.
We cannot just implement “==” or “===” operator to compare two objects. The better way to do the comparison is to use JSON. Stringify and compare the objects.
Comparing objects is easy, use === or Object.is(). This function returns true if they have the same reference and false if they do not. Again, let me stress, it is comparing the references to the objects, not the keys and values of the objects. So, from Example 3, Object.is(obj1,obj2); would return false.
Version with no ES6 features that runs in quadratic time:
function deepGraphEqual(a, b) {
var left = [], right = [], has = Object.prototype.hasOwnProperty;
function visit(a, b) {
var i, k;
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
return a === b;
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
return false;
for (i = 0; i < left.length; i++) {
if (a === left[i])
return b === right[i];
if (b === right[i])
return a === left[i];
}
for (k in a)
if (has.call(a, k) && !has.call(b, k))
return false;
for (k in b)
if (has.call(b, k) && !has.call(a, k))
return false;
left.push(a);
right.push(b);
for (k in a)
if (has.call(a, k) && !visit(a[k], b[k]))
return false;
return true;
}
return visit(a, b);
}
Version with ES6 Map
that runs in linear time:
function deepGraphEqual(a, b) {
let left = new Map(), right = new Map(), has = Object.prototype.hasOwnProperty;
function visit(a, b) {
if (typeof a !== 'object' || typeof b !== 'object' || a === null || b === null)
return a === b;
if (Object.getPrototypeOf(a) !== Object.getPrototypeOf(b))
return false;
if (left.has(a))
return left.get(a) === b
if (right.has(b))
return right.get(b) === a
for (let k in a)
if (has.call(a, k) && !has.call(b, k))
return false;
for (let k in b)
if (has.call(b, k) && !has.call(a, k))
return false;
left.set(a, b);
right.set(b, a);
for (let k in a)
if (has.call(a, k) && !visit(a[k], b[k]))
return false;
return true;
}
return visit(a, b);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With