Object Comparison in JavaScript

Object comparison in JavaScript

Unfortunately there is no perfect way, unless you use _proto_ recursively and access all non-enumerable properties, but this works in Firefox only.

So the best I can do is to guess usage scenarios.


1) Fast and limited.

Works when you have simple JSON-style objects without methods and DOM nodes inside:

 JSON.stringify(obj1) === JSON.stringify(obj2) 

The ORDER of the properties IS IMPORTANT, so this method will return false for following objects:

 x = {a: 1, b: 2};
y = {b: 2, a: 1};

2) Slow and more generic.

Compares objects without digging into prototypes, then compares properties' projections recursively, and also compares constructors.

This is almost correct algorithm:

function deepCompare () {
var i, l, leftChain, rightChain;

function compare2Objects (x, y) {
var p;

// remember that NaN === NaN returns false
// and isNaN(undefined) returns true
if (isNaN(x) && isNaN(y) && typeof x === 'number' && typeof y === 'number') {
return true;
}

// Compare primitives and functions.
// Check if both arguments link to the same object.
// Especially useful on the step where we compare prototypes
if (x === y) {
return true;
}

// Works in case when functions are created in constructor.
// Comparing dates is a common scenario. Another built-ins?
// We can even handle functions passed across iframes
if ((typeof x === 'function' && typeof y === 'function') ||
(x instanceof Date && y instanceof Date) ||
(x instanceof RegExp && y instanceof RegExp) ||
(x instanceof String && y instanceof String) ||
(x instanceof Number && y instanceof Number)) {
return x.toString() === y.toString();
}

// At last checking prototypes as good as we can
if (!(x instanceof Object && y instanceof Object)) {
return false;
}

if (x.isPrototypeOf(y) || y.isPrototypeOf(x)) {
return false;
}

if (x.constructor !== y.constructor) {
return false;
}

if (x.prototype !== y.prototype) {
return false;
}

// Check for infinitive linking loops
if (leftChain.indexOf(x) > -1 || rightChain.indexOf(y) > -1) {
return false;
}

// Quick checking of one object being a subset of another.
// todo: cache the structure of arguments[0] for performance
for (p in y) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}
}

for (p in x) {
if (y.hasOwnProperty(p) !== x.hasOwnProperty(p)) {
return false;
}
else if (typeof y[p] !== typeof x[p]) {
return false;
}

switch (typeof (x[p])) {
case 'object':
case 'function':

leftChain.push(x);
rightChain.push(y);

if (!compare2Objects (x[p], y[p])) {
return false;
}

leftChain.pop();
rightChain.pop();
break;

default:
if (x[p] !== y[p]) {
return false;
}
break;
}
}

return true;
}

if (arguments.length < 1) {
return true; //Die silently? Don't know how to handle such case, please help...
// throw "Need two or more arguments to compare";
}

for (i = 1, l = arguments.length; i < l; i++) {

leftChain = []; //Todo: this can be cached
rightChain = [];

if (!compare2Objects(arguments[0], arguments[i])) {
return false;
}
}

return true;
}

Known issues (well, they have very low priority, probably you'll never notice them):

  • objects with different prototype structure but same projection
  • functions may have identical text but refer to different closures

Tests: passes tests are from How to determine equality for two JavaScript objects?.

Is object comparison in javaScript in linear or constant time?

In JS, objects are compared by references (not structural/deep equality). Because only a fixed-length memory address is being compared, the comparison is O(1).

For example, the below code snippet always prints false even though every key/value pair across obj1 and obj2 are the same. obj1 and obj2 do not refer to the same underlying object, and objects are compared by reference, not keys and values.

let obj1 = { a: 1, b: 2 };
let obj2 = { a: 1, b: 2 };
console.log(obj1 == obj2 || obj1 === obj2); // => false

How to determine equality for two JavaScript objects?

The short answer

The simple answer is: No, there is no generic means to determine that an object is equal to another in the sense you mean. The exception is when you are strictly thinking of an object being typeless.

The long answer

The concept is that of an Equals method that compares two different instances of an object to indicate whether they are equal at a value level. However, it is up to the specific type to define how an Equals method should be implemented. An iterative comparison of attributes that have primitive values may not be enough: an object may contain attributes which are not relevant to equality. For example,

 function MyClass(a, b)
{
var c;
this.getCLazy = function() {
if (c === undefined) c = a * b // imagine * is really expensive
return c;
}
}

In this above case, c is not really important to determine whether any two instances of MyClass are equal, only a and b are important. In some cases c might vary between instances and yet not be significant during comparison.

Note this issue applies when members may themselves also be instances of a type and these each would all be required to have a means of determining equality.

Further complicating things is that in JavaScript the distinction between data and method is blurred.

An object may reference a method that is to be called as an event handler, and this would likely not be considered part of its 'value state'. Whereas another object may well be assigned a function that performs an important calculation and thereby makes this instance different from others simply because it references a different function.

What about an object that has one of its existing prototype methods overridden by another function? Could it still be considered equal to another instance that it otherwise identical? That question can only be answered in each specific case for each type.

As stated earlier, the exception would be a strictly typeless object. In which case the only sensible choice is an iterative and recursive comparison of each member. Even then one has to ask what is the 'value' of a function?

Fastest way to compare 2 objects in js

You can have a look at the fast-deep-equal package. Here is a performance benchmark from their README.md for your reference.

fast-deep-equal x 226,960 ops/sec ±1.55% (86 runs sampled)
nano-equal x 218,210 ops/sec ±0.79% (89 runs sampled)
shallow-equal-fuzzy x 206,762 ops/sec ±0.84% (88 runs sampled)
underscore.isEqual x 128,668 ops/sec ±0.75% (91 runs sampled)
lodash.isEqual x 44,895 ops/sec ±0.67% (85 runs sampled)
deep-equal x 51,616 ops/sec ±0.96% (90 runs sampled)
deep-eql x 28,218 ops/sec ±0.42% (85 runs sampled)
assert.deepStrictEqual x 1,777 ops/sec ±1.05% (86 runs sampled)
ramda.equals x 13,466 ops/sec ±0.82% (86 runs sampled)
The fastest is fast-deep-equal

compare two objects without object reference Angular 10

JavaScript does not have a built-in property equality operator for objects and arrays.

A simple way to check if objects are equal is using JSON.stringify:

JSON.stringify(objectA) === JSON.stringify(objectB);

This will convert the objects into strings and makes the easily comparable. This approach also works well when the objects are nested.

Another option would be to use an equals method (or better a deep equal method that also works for nested objects), that iterates over all the objects' properties and compares their values.

Best way to compare Objects on the top Level

you can look in google for "shallow equality" like here
https://dmitripavlutin.com/how-to-compare-objects-in-javascript/#3-shallow-equality

this link as some cool way of doing it too
https://www.samanthaming.com/tidbits/33-how-to-compare-2-objects/#es6-way-for-comparing-2-objects

but your solution is fine. if you would like an other way of writing it you can do

const isSame = (oldItem, newItem) => 
["id", "name", "coverURL", "year"].find(prop => oldItem[prop] !== newItem[prop]) == null;

Deep equal object comparison in JavaScript

This line in your code...

return deepEqual(obj1[key], obj2[c]);

... basically makes the comparison function to stop when the first nested object is encountered. What should happen instead is 1) result of deepEqual is calculated, and 2) only if it's false, the function returns it immediately. For example:

const isDeeplyEqual = deepEqual(obj1[key], obj2[c]);
if (!isDeeplyEqual) return false;

... or just:

if (!deepEqual(obj1[key], obj2[c])) return false;

Comparing an two objects with arrays for quality

I fixed it using the following function, this will only work if the two objects have the same structure.

const _ = require("lodash");

const filtersA = {
brands: ["5f1d5077b261aa63f42cc8a9", "5f1d5077b261aa63f42cc8aa"],
models: [],
};

const filtersB = {
brands: ["5f1d5077b261aa63f42cc8aa", "5f1d5077b261aa63f42cc8a9"],
models: [],
};

/**
* Checks if two objects with arrays are equal
*
* @param {Object} a
* @param {Object} b
*/
const areObjectWithArraysEqual = (a, b) => Object.keys(a).every((key) => _.isEqual(a[key].sort(), b[key].sort()));

console.log(areObjectWithArraysEqual(filtersA, filtersB));

This returns true as expected

Why do you need deep object comparison?

You answered your own question! The very fact that you had to say "assume reference: 1234" in your example says that you know that a shallow compare will not work if propObj1 pointed to distinct but equal values (i.e. your exact example but without the "assume reference: 1234" comment.

objA and objB below will fail a shallow equals check, but pass a deep equals check:

const objA = {
propA: 123
propB: 'a'
propObj1: {
propObj1: 'abc'
}
}

const objB = {
propA: 123
propB: 'a'
propObj1: {
propObj1: 'abc'
}
}


Related Topics



Leave a reply



Submit