javascriptobjectcomparisonobject-comparison

Object comparison in JavaScript


What is the best way to compare objects in JavaScript?

Example:

var user1 = {name : "nerd", org: "dev"};
var user2 = {name : "nerd", org: "dev"};
var eq = user1 == user2;
alert(eq); // gives false

I know that two objects are equal if they refer to the exact same object, but is there a way to check if they have the same attributes' values?

The following way works for me, but is it the only possibility?

var eq = Object.toJSON(user1) == Object.toJSON(user2);
alert(eq); // gives true

Solution

  • 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):

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