Explain Codes LogoExplain Codes Logo

How can I determine equality for two JavaScript objects?

javascript
lodash
deep-comparison
es6
Alex KataevbyAlex Kataev·Aug 11, 2024
TLDR

For a quickie, use JSON.stringify:

const isEqual = JSON.stringify(obj1) === JSON.stringify(obj2); // Neither fast, nor furious if objects have different key orders or methods!

For a more rigorous root check, compare properties recursively:

function deepEqual(a, b) { if (a === b) return true; // Twins, not just siblings if (a == null || b == null || typeof a != 'object' || typeof b != 'object') return false; // Mom? Dad? let keysA = Object.keys(a), keysB = Object.keys(b); if (keysA.length != keysB.length) return false; // Eye for an eye, key for a key for (let key of keysA) { if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false; // Keys but no locksmith } return true; // Emoji cheers🎉🎉 but no beers🍺🍺 }

Use JSON.stringify for a quick surface snapshot; deepEqual for a thorough investigation.

Tap into lodash: a developer's Swiss-knife

Lodash's _.isEqual function is like that all-knowing friend who never fails to surprise you. It provides a robust solution for deep object comparisons. Borrow the wisdom:

const isEqual = require('lodash').isEqual(obj1, obj2); // Lodash: Because you have better things to do than reinvent solutions

It's like comparing DNAs, even the most deeply nested genes don't escape lodash's scrutiny.

Roll your own: object-specific comparison

Sometimes, you need a custom equals(other) method. You define the rules of the game:

class MyObject { constructor(data) { this.data = data; // It's a constructor, not a deconstructor } equals(other) { // DIY comparison logic, you're the boss here! return deepEqual(this.data, other.data); } }

Special objects need special attention:

  • Date Objects: In the world of dates, it's the timestamp that counts, not the fancy formatting!
  • Functions: Sorry functions, you're out of the comparison game, too complex to handle.
  • Arrays: Special creatures but still objects, treat accordingly.
// It's a date...or is it? if (obj1 instanceof Date && obj2 instanceof Date) { equals = obj1.valueOf() === obj2.valueOf(); // Check their timestamps, not their outfits } // Oh look, arrays wanna play too! equals = Array.isArray(obj1) && Array.isArray(obj2) && obj1.length === obj2.length && obj1.every((value, index) => deepEqual(value, obj2[index])); // No child(left) behind!

ABA: Always Be Adapting with ES6 features

Modern JavaScript is your friend. Destructuring, spread, Arrow functions makes your code concise and understandable:

const deepEqual = (a, b) => { // Naughty or nice, they're all the same to me! if (a === b) return true; if (a && b && typeof a === 'object' && typeof b === 'object') { let keys = [Object.keys(a), Object.keys(b)]; // Because order and details matter in a heist...err...comparison! return keys[0].every(key => keys[1].includes(key) && deepEqual(a[key], b[key])); } return false; };

Bring out the big guns: lodash for comparison

When the going gets tough, the tough get lodash. Comparing nested objects? Check. Bugs? Squashed. Say hello to reliability and goodbye to redundancies!

const _ = require('lodash'); const isEqual = _.isEqual(object1, object2); // Who needs Sherlock when you've got Lodash?

Invoke the lodash's _.isEqual for maximum precision and minimum effort.

Build for resilience: handle edge cases

You're on a quest for truth. Be prepared to tackle special object types (Dates, Arrays, Functions, Regex). For each, there's a unique path to truth:

  • Dates: Use valueOf to check timestamp equality.
  • Arrays: They are objects too! Use object comparison.
  • Functions: Often omitted due to complexity of comparing behaviors.
  • Regular expressions: Convert to string before comparison.