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
JavaScript Trick For 'Paste as Plain Text' in Execcommand
Adding Additional Data to Select Options Using Jquery
Dynamically Loading CSS Stylesheet Doesn't Work on Ie
Jquery Event Won't Fire After Ajax Call
Get Iframe'S Document, from JavaScript in Main Document
How to Automatically Reload a Web Page At a Certain Time
How to Get the Background Color of an HTML Element
How to Open a Link in New Tab (And Not New Window)
Does Schema.Org Markup Work If Markup Is Dynamically Built With JavaScript
How to Refer to JavaScript Variables Across Webpages in a Browser Session
Html5 Dragleave Fired When Hovering a Child Element
Select All Elements With a "Data-Xxx" Attribute Without Using Jquery
Why Is Using the JavaScript Eval Function a Bad Idea
How to Merge Properties of Two JavaScript Objects Dynamically
JavaScript Infamous Loop Issue
How to Detect a Click Outside an Element
Difference Between Json and Object Literal Notation
Scrollable Div to Stick to Bottom, When Outer Div Changes in Size