Search code examples
javascriptalgorithmobjectcomparisonequality

Is there a better way to deeply compare two objects?


I would like to know if there is a better way to compare objects in JavaScript.

I understand that there are many large solutions but I would like to get a clear answer if their is a simple way to do this without an external lib. Possibly using one of the newer APIs.

I also understand it is likely that the answer is simply: no, there is no better solution yet.

This is the base of the example I have:


const a = {value: 'foo'};
const b = {value: 'foo'};

function isEqual(obj1, obj2) {
 // This function is the only being changed in the examples
 // It should return a boolean
 return // TODO
}

I know this does not work because its comparing the memory reference:

function isEqual(obj1, obj2) {
 return obj1 === obj2;
}
console.log(isEqual(a, b)); // false

However, I can do this:

function isEqual(obj1, obj2) {
 return JSON.stringify(obj1) === JSON.stringify(obj2);
}
console.log(isEqual(a, b)); // true

JavaScript has come a long way so can it do this natively. I know you can so it using external libs like lodash, but i would like ot know if there is a better way using modern JavaScript.

I attempted to investigate it and did not find a valid solution, here are some links related to this topic.


Solution

  • The Q&A you refer to include also solutions in plain JavaScript without external libraries, but they differ in several ways. Which one to choose is a matter of opinion, and depends on the actual need you have. The thing is that equality is a fuzzy concept. Some will regard some objects the same while others wouldn't, and so choices have to be made.

    Some examples, just scratching the surface, of what could be considered equal or not, depending on your need/expectation:

    • new Set([{}]) and new Set([{}]): if they are regarded equal, note how the member of the first set is not in the second.
    • function () { } and function () { }
    • { a: 1, b: 2} and { b: 2, a: 1 }: if we regard a plain object as a collection of key/value pairs that have no order, then you would regard these the same, but maybe you have an algorithm that relies on the insertion order of non-array-index keys (whether that is good practice is debatable), and then these objects should not be regarded the same.
    • {} and new Proxy({}, {})
    • When a=[1,2,3], then consider a.values() and a.values(): two iterators on the same array.
    • /a/g and /a/g, recalling that regexes have state (lastIndex).
    • document.createTextElement("") and document.createTextElement(""): If considered the same, they surely stop being that when one of both gets inserted in the document.
    • new MyClass() and new MyClass() when they have a private field that is initialised in the constructor as a random value. As such field defines the object state, but cannot be queried by a function outside the class's definition, you cannot really decide on equality.
    • ... one could come up with a lot more examples.

    In conclusion, you'd need to make quite a few decisions, which could include the decision that your function will only need to work for a limited set of "types" of objects.