[removed] Deep comparison recursively: Objects and properties

后端 未结 3 1976
别跟我提以往
别跟我提以往 2021-01-13 08:06

Today I finished reading Ch. 4 in Eloquent JS and I\'m struggling to understand how to perform a deep comparison between objects and their properties, particularly through t

相关标签:
3条回答
  • 2021-01-13 08:20

    You can use below code for deep comparison -

    const isEqual = (a, b) => {
      if (a === b) return true;
      if (a instanceof Date && b instanceof Date) return a.getTime() === b.getTime();
      if (!a || !b || (typeof a !== 'object' && typeof b !== 'object')) return a === b;
      if (a === null || a === undefined || b === null || b === undefined) return false;
      if (a.prototype !== b.prototype) return false;
      let keys = Object.keys(a);
      if (keys.length !== Object.keys(b).length) return false;
      return keys.every(k => isEqual(a[k], b[k]));
    };
    

    example -

    isEqual({ prop1: [2, { e: 3 }], prop2: [4], prop3: 'foo' }, { prop1: [2, { e: 3 }], prop2: [4], prop3: 'foo' }); // true
    
    0 讨论(0)
  • 2021-01-13 08:38

    Check the code below. That should work for you.

    function objectEquals(x, y) {
        'use strict';
    
        if (x === null || x === undefined || y === null || y === undefined) { return x === y; }
        // after this just checking type of one would be enough
        if (x.constructor !== y.constructor) { return false; }
        // if they are functions, they should exactly refer to same one (because of closures)
        if (x instanceof Function) { return x === y; }
        // if they are regexps, they should exactly refer to same one (it is hard to better equality check on current ES)
        if (x instanceof RegExp) { return x === y; }
        if (x === y || x.valueOf() === y.valueOf()) { return true; }
        if (Array.isArray(x) && x.length !== y.length) { return false; }
    
        // if they are dates, they must had equal valueOf
        if (x instanceof Date) { return false; }
    
        // if they are strictly equal, they both need to be object at least
        if (!(x instanceof Object)) { return false; }
        if (!(y instanceof Object)) { return false; }
    
        // recursive object equality check
        var p = Object.keys(x);
        return Object.keys(y).every(function (i) { return p.indexOf(i) !== -1; }) &&
            p.every(function (i) { return objectEquals(x[i], y[i]); });
    }
    
    0 讨论(0)
  • 2021-01-13 08:43

    Probably easiest to just post a function that does the job with plenty of comments. The recursive part is near the bottom, in the function passed to every:

    // Helper to return a value's internal object [[Class]]
    // That this returns [object Type] even for primitives
    function getClass(obj) {
      return Object.prototype.toString.call(obj);
    }
    
    /*
    ** @param a, b        - values (Object, RegExp, Date, etc.)
    ** @returns {boolean} - true if a and b are the object or same primitive value or
    **                      have the same properties with the same values
    */
    function objectTester(a, b) {
    
      // If a and b reference the same value, return true
      if (a === b) return true;
    
      // If a and b aren't the same type, return false
      if (typeof a != typeof b) return false;
    
      // Already know types are the same, so if type is number
      // and both NaN, return true
      if (typeof a == 'number' && isNaN(a) && isNaN(b)) return true;
    
      // Get internal [[Class]]
      var aClass = getClass(a);
      var bClass = getClass(b)
    
      // Return false if not same class
      if (aClass != bClass) return false;
    
      // If they're Boolean, String or Number objects, check values
      if (aClass == '[object Boolean]' || aClass == '[object String]' || aClass == '[object Number]') {
        return a.valueOf() == b.valueOf();
      }
    
      // If they're RegExps, Dates or Error objects, check stringified values
      if (aClass == '[object RegExp]' || aClass == '[object Date]' || aClass == '[object Error]') {
        return a.toString() == b.toString();
      }
    
      // Otherwise they're Objects, Functions or Arrays or some kind of host object
      if (typeof a == 'object' || typeof a == 'function') {
    
        // For functions, check stringigied values are the same
        // Almost certainly false if a and b aren't trivial
        // and are different functions
        if (aClass == '[object Function]' && a.toString() != b.toString()) return false;
    
        var aKeys = Object.keys(a);
        var bKeys = Object.keys(b);
    
        // If they don't have the same number of keys, return false
        if (aKeys.length != bKeys.length) return false;
    
        // Check they have the same keys
        if (!aKeys.every(function(key){return b.hasOwnProperty(key)})) return false;
    
        // Check key values - uses ES5 Object.keys
        return aKeys.every(function(key){
          return objectTester(a[key], b[key])
        });
      }
      return false;
    }
    

    The tests on Date, RegExp, Error, etc. should probably return false if the values/strings aren't the same then fall through to the property checks, but do that only if you think someone might attach properties to Number objects (it's extremely rare to use Number objects, much less add properties to them, but I guess it might happen).

    Here it is:

    /*
    ** @param a, b        - values (Object, RegExp, Date, etc.)
    ** @returns {boolean} - true if a and b are the object or same primitive value or
    **                      have the same properties with the same values
    */
    function objectTester(a, b) {
    
      // If a and b reference the same value, return true
      if (a === b) return true;
    
      // If a and b aren't the same type, return false
      if (typeof a != typeof b) return false;
    
      // Already know types are the same, so if type is number
      // and both NaN, return true
      if (typeof a == 'number' && isNaN(a) && isNaN(b)) return true;
    
      // Get internal [[Class]]
      var aClass = getClass(a);
      var bClass = getClass(b)
    
      // Return false if not same class
      if (aClass != bClass) return false;
    
      // If they're Boolean, String or Number objects, check values
      if (aClass == '[object Boolean]' || aClass == '[object String]' || aClass == '[object Number]') {
        if (a.valueOf() != b.valueOf()) return false;
      }
    
      // If they're RegExps, Dates or Error objects, check stringified values
      if (aClass == '[object RegExp]' || aClass == '[object Date]' || aClass == '[object Error]') {
        if (a.toString() != b.toString()) return false;
      }
    
      // For functions, check stringigied values are the same
      // Almost impossible to be equal if a and b aren't trivial
      // and are different functions
      if (aClass == '[object Function]' && a.toString() != b.toString()) return false;
    
      // For all objects, (including Objects, Functions, Arrays and host objects),
      // check the properties
      var aKeys = Object.keys(a);
      var bKeys = Object.keys(b);
    
      // If they don't have the same number of keys, return false
      if (aKeys.length != bKeys.length) return false;
    
      // Check they have the same keys
      if (!aKeys.every(function(key){return b.hasOwnProperty(key)})) return false;
    
      // Check key values - uses ES5 Object.keys
      return aKeys.every(function(key){
        return objectTester(a[key], b[key])
      });
      return false;
    }
    
    0 讨论(0)
提交回复
热议问题