Как определить равенство для двух объектов JavaScript? - PullRequest
547 голосов
/ 14 октября 2008

Оператор строгого равенства сообщит вам, если два объекта типов равны. Однако, есть ли способ определить, равны ли два объекта, так же, как значение хеш-кода в Java?

Вопрос переполнения стека Есть ли какая-либо функция hashCode в JavaScript? похожа на этот вопрос, но требует более академического ответа. Сценарий, приведенный выше, демонстрирует, почему это необходимо, и мне интересно, есть ли какое-нибудь эквивалентное решение .

Ответы [ 55 ]

0 голосов
/ 26 апреля 2019

function isDeepEqual(obj1, obj2, testPrototypes = false) {
  if (obj1 === obj2) {
    return true
  }

  if (typeof obj1 === "function" && typeof obj2 === "function") {
    return obj1.toString() === obj2.toString()
  }

  if (obj1 instanceof Date && obj2 instanceof Date) {
    return obj1.getTime() === obj2.getTime()
  }

  if (
    Object.prototype.toString.call(obj1) !==
      Object.prototype.toString.call(obj2) ||
    typeof obj1 !== "object"
  ) {
    return false
  }

  const prototypesAreEqual = testPrototypes
    ? isDeepEqual(
        Object.getPrototypeOf(obj1),
        Object.getPrototypeOf(obj2),
        true
      )
    : true

  const obj1Props = Object.getOwnPropertyNames(obj1)
  const obj2Props = Object.getOwnPropertyNames(obj2)

  return (
    obj1Props.length === obj2Props.length &&
    prototypesAreEqual &&
    obj1Props.every(prop => isDeepEqual(obj1[prop], obj2[prop]))
  )
}

console.log(isDeepEqual({key: 'one'}, {key: 'first'}))
console.log(isDeepEqual({key: 'one'}, {key: 'one'}))
0 голосов
/ 12 марта 2015

Да, еще один ответ ...

Object.prototype.equals = function (object) {
    if (this.constructor !== object.constructor) return false;
    if (Object.keys(this).length !== Object.keys(object).length) return false;
    var obk;
    for (obk in object) {
        if (this[obk] !== object[obk])
            return false;
    }
    return true;
}

var aaa = JSON.parse('{"name":"mike","tel":"1324356584"}');
var bbb = JSON.parse('{"tel":"1324356584","name":"mike"}');
var ccc = JSON.parse('{"name":"mike","tel":"584"}');
var ddd = JSON.parse('{"name":"mike","tel":"1324356584", "work":"nope"}');

$("#ab").text(aaa.equals(bbb));
$("#ba").text(bbb.equals(aaa));
$("#bc").text(bbb.equals(ccc));
$("#ad").text(aaa.equals(ddd));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
aaa equals bbb? <span id="ab"></span> <br/>
bbb equals aaa? <span id="ba"></span> <br/>
bbb equals ccc? <span id="bc"></span> <br/>
aaa equals ddd? <span id="ad"></span>
0 голосов
/ 29 августа 2017

Я реализовал метод, который принимает два jsons и проверяет, имеют ли их ключи одинаковые значения, используя рекурсию. Для решения этой проблемы я использовал другой вопрос .

const arraysEqual = (a, b) => {
    if (a === b)
        return true;
    if (a === null || b === null)
        return false;
    if (a.length !== b.length)
        return false;

    // If you don't care about the order of the elements inside
    // the array, you should sort both arrays here.

    for (let i = 0; i < a.length; ++i) {
        if (a[i] !== b[i])
            return false;
    }
    return true;
};

const jsonsEqual = (a, b) => {

  if(typeof a !== 'object' || typeof b !== 'object')
      return false;

  if (Object.keys(a).length === Object.keys(b).length) { // if items have the same size
      let response = true;

      for (let key in a) {
          if (!b[key]) // if not key
              response = false;
          if (typeof a[key] !== typeof b[key]) // if typeof doesn't equals
              response = false;
          else {
              if (Array.isArray(a[key])) // if array
                  response = arraysEqual(a[key], b[key]);
              else if (typeof a[key] === 'object') // if another json
                  response = jsonsEqual(a[key], b[key]);
              else if (a[key] !== b[key])  // not equals
                  response = false;
          }
          if (!response) // return if one item isn't equal
              return false;
      }
  } else
      return false;

  return true;
};

const json1 = { 
  a: 'a', 
  b: 'asd', 
  c: [
    '1',
    2,
    2.5,
    '3', 
    {
      d: 'asd',
      e: [
        1.6,
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const json2 = {
  a: 'nops',
  b: 'asd'
};

const json3 = { 
  a: 'h', 
  b: '484', 
  c: [
    3,
    4.5,
    '2ss', 
    {
      e: [
        { 
          f: 'asdasd',
          g: '123'
        }
      ]
    }
  ],
  h: 1,
  i: 1.2,
};

const result = jsonsEqual(json1,json2);
//const result = jsonsEqual(json1,json3);
//const result = jsonsEqual(json1,json1);

if(result) // is equal
  $('#result').text("Jsons are the same")
else
  $('#result').text("Jsons aren't equals")
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div id="result"></div>
0 голосов
/ 23 июня 2013

Некоторые из следующих решений имеют проблемы с производительностью, функциональностью и стилем ... Они недостаточно продуманы, а некоторые из них не подходят для разных случаев. Я попытался решить эту проблему в своем собственном решении, и я был бы очень признателен за ваш отзыв:

http://stamat.wordpress.com/javascript-object-comparison/

//Returns the object's class, Array, Date, RegExp, Object are of interest to us
var getClass = function(val) {
    return Object.prototype.toString.call(val)
        .match(/^\[object\s(.*)\]$/)[1];
};

//Defines the type of the value, extended typeof
var whatis = function(val) {

    if (val === undefined)
        return 'undefined';
    if (val === null)
        return 'null';

    var type = typeof val;

    if (type === 'object')
        type = getClass(val).toLowerCase();

    if (type === 'number') {
        if (val.toString().indexOf('.') > 0)
            return 'float';
        else
        return 'integer';
    }

    return type;
   };

var compareObjects = function(a, b) {
    if (a === b)
        return true;
    for (var i in a) {
        if (b.hasOwnProperty(i)) {
            if (!equal(a[i],b[i])) return false;
        } else {
            return false;
        }
    }

    for (var i in b) {
        if (!a.hasOwnProperty(i)) {
            return false;
        }
    }
    return true;
};

var compareArrays = function(a, b) {
    if (a === b)
        return true;
    if (a.length !== b.length)
        return false;
    for (var i = 0; i < a.length; i++){
        if(!equal(a[i], b[i])) return false;
    };
    return true;
};

var _equal = {};
_equal.array = compareArrays;
_equal.object = compareObjects;
_equal.date = function(a, b) {
    return a.getTime() === b.getTime();
};
_equal.regexp = function(a, b) {
    return a.toString() === b.toString();
};
//  uncoment to support function as string compare
//  _equal.fucntion =  _equal.regexp;



/*
 * Are two values equal, deep compare for objects and arrays.
 * @param a {any}
 * @param b {any}
 * @return {boolean} Are equal?
 */
var equal = function(a, b) {
    if (a !== b) {
        var atype = whatis(a), btype = whatis(b);

        if (atype === btype)
            return _equal.hasOwnProperty(atype) ? _equal[atype](a, b) : a==b;

        return false;
    }

    return true;
};
0 голосов
/ 13 февраля 2014

Проверка на равенство объектов: JSON.stringify(array1.sort()) === JSON.stringify(array2.sort())

Вышеупомянутый тест также работает с массивами объектов, в этом случае используется функция сортировки, как описано в http://www.w3schools.com/jsref/jsref_sort.asp

Может быть достаточно для небольших массивов с плоскими схемами JSON.

0 голосов
/ 03 мая 2017

Вот общая функция проверки равенства, которая получает массив элементов в качестве входных данных и сравнивает их друг с другом. Работает со всеми типами элементов.

const isEqual = function(inputs = []) {
  // Checks an element if js object.
  const isObject = function(data) {
    return Object.prototype.toString.call(data) === '[object Object]';
  };
  // Sorts given object by its keys.
  const sortObjectByKey = function(obj) {
    const self = this;
    if (!obj) return {};
    return Object.keys(obj).sort().reduce((initialVal, item) => {
      initialVal[item] = !Array.isArray(obj[item]) &&
        typeof obj[item] === 'object'
        ? self.objectByKey(obj[item])
        : obj[item];
      return initialVal;
    }, {});
  };

  // Checks equality of all elements in the input against each other. Returns true | false
  return (
    inputs
      .map(
        input =>
          typeof input == 'undefined'
            ? ''
            : isObject(input)
                ? JSON.stringify(sortObjectByKey(input))
                : JSON.stringify(input)
      )
      .reduce(
        (prevValue, input) =>
          prevValue === '' || prevValue === input ? input : false,
        ''
      ) !== false
  );
};

// Tests (Made with Jest test framework.)
test('String equality check', () => {
  expect(isEqual(['murat'])).toEqual(true);
  expect(isEqual(['murat', 'john', 'doe'])).toEqual(false);
  expect(isEqual(['murat', 'murat', 'murat'])).toEqual(true);
});

test('Float equality check', () => {
  expect(isEqual([7.89, 3.45])).toEqual(false);
  expect(isEqual([7, 7.50])).toEqual(false);
  expect(isEqual([7.50, 7.50])).toEqual(true);
  expect(isEqual([7, 7])).toEqual(true);
  expect(isEqual([0.34, 0.33])).toEqual(false);
  expect(isEqual([0.33, 0.33])).toEqual(true);
});

test('Array equality check', () => {
  expect(isEqual([[1, 2, 3], [1, 2, 3]])).toEqual(true);
  expect(isEqual([[1, 3], [1, 2, 3]])).toEqual(false);
  expect(isEqual([['murat', 18], ['murat', 18]])).toEqual(true);
});

test('Object equality check', () => {
  let obj1 = {
    name: 'murat',
    age: 18
  };
  let obj2 = {
    name: 'murat',
    age: 18
  };
  let obj3 = {
    age: 18,
    name: 'murat'
  };
  let obj4 = {
    name: 'murat',
    age: 18,
    occupation: 'nothing'
  };
  expect(isEqual([obj1, obj2])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3])).toEqual(true);
  expect(isEqual([obj1, obj2, obj3, obj4])).toEqual(false);
});

test('Weird equality checks', () => {
  expect(isEqual(['', {}])).toEqual(false);
  expect(isEqual([0, '0'])).toEqual(false);
});

Есть также суть

0 голосов
/ 18 февраля 2017

Вот мое решение этой проблемы. Я не считаю, что мое замечательно, но оно работает при сравнении объектов с любым типом

Object.prototype.fullMatch = function(obj){
    if (typeof this !== typeof obj) return false;
    if (this == null && obj != null || this != null && obj == null) return false;
    var this_keys = [];
    var obj_keys = [];
    for (var key in this) if (this.hasOwnProperty(key)) this_keys.push(key);
    for (var key in obj) if (obj.hasOwnProperty(key)) obj_keys.push(key);
    if (this_keys.length !== obj_keys.length){
        this_keys = null;
        obj_keys = null;
        return false;
    }
    var full_match = true;
    for (var key in this){
        if (this.hasOwnProperty(key) && obj.hasOwnProperty(key)){
            var this_value = this[key];
            var obj_value = obj[key];
            if (typeof this_value !== typeof obj_value || ("object" === typeof this_value && !this_value.fullMatch(obj_value)) || "object" !== typeof this_value && this_value !== obj_value){
                full_match = false;
                break;
            }
        }
    }
    return full_match;
};
0 голосов
/ 19 ноября 2014

Конечно, пока мы на нем, я добавлю свое собственное изобретение колеса (я горжусь количеством спиц и используемых материалов):

////////////////////////////////////////////////////////////////////////////////

var equals = function ( objectA, objectB ) {
    var result = false,
        keysA,
        keysB;

    // Check if they are pointing at the same variable. If they are, no need to test further.
    if ( objectA === objectB ) {
        return true;
    }

    // Check if they are the same type. If they are not, no need to test further.
    if ( typeof objectA !== typeof objectB ) {
        return false;
    }

    // Check what kind of variables they are to see what sort of comparison we should make.
    if ( typeof objectA === "object" ) {
        // Check if they have the same constructor, so that we are comparing apples with apples.
        if ( objectA.constructor === objectA.constructor ) {
            // If we are working with Arrays...
            if ( objectA instanceof Array ) {
                // Check the arrays are the same length. If not, they cannot be the same.
                if ( objectA.length === objectB.length ) {
                    // Compare each element. They must be identical. If not, the comparison stops immediately and returns false.
                    return objectA.every(
                        function ( element, i ) {
                            return equals( element, objectB[ i ] );
                        }
                    );
                }
                // They are not the same length, and so are not identical.
                else {
                    return false;
                }
            }
            // If we are working with RegExps...
            else if ( objectA instanceof RegExp ) {
                // Return the results of a string comparison of the expression.
                return ( objectA.toString() === objectB.toString() );
            }
            // Else we are working with other types of objects...
            else {
                // Get the keys as arrays from both objects. This uses Object.keys, so no old browsers here.
                keysA = Object.keys( objectA );

                keysB = Object.keys( objectB );

                // Check the key arrays are the same length. If not, they cannot be the same.
                if ( keysA.length === keysB.length ) {
                    // Compare each property. They must be identical. If not, the comparison stops immediately and returns false.
                    return keysA.every(
                        function ( element ) {
                            return equals( objectA[ element ], objectB[ element ] );
                        }
                    );
                }
                // They do not have the same number of keys, and so are not identical.
                else {
                    return false;
                }
            }
        }
        // They don't have the same constructor.
        else {
            return false;
        }
    }
    // If they are both functions, let us do a string comparison.
    else if ( typeof objectA === "function" ) {
        return ( objectA.toString() === objectB.toString() );
    }
    // If a simple variable type, compare directly without coercion.
    else {
        return ( objectA === objectB );
    }

    // Return a default if nothing has already been returned.
    return result;
};

////////////////////////////////////////////////////////////////////////////////

Возвращает false как можно быстрее, но, конечно, для большого объекта, где различие глубоко вложено, оно может быть менее эффективным. В моем собственном сценарии важна хорошая обработка вложенных массивов.

Надеюсь, это поможет кому-то, нуждающемуся в таком "колесе".

0 голосов
/ 24 сентября 2016

Моя версия, которая включает в себя цепочку, где находится разница, и в чем разница.

function DeepObjectCompare(O1, O2)
{
    try {
        DOC_Val(O1, O2, ['O1->O2', O1, O2]);
        return DOC_Val(O2, O1, ['O2->O1', O1, O2]);
    } catch(e) {
        console.log(e.Chain);
        throw(e);
    }
}
function DOC_Error(Reason, Chain, Val1, Val2)
{
    this.Reason=Reason;
    this.Chain=Chain;
    this.Val1=Val1;
    this.Val2=Val2;
}

function DOC_Val(Val1, Val2, Chain)
{
    function DoThrow(Reason, NewChain) { throw(new DOC_Error(Reason, NewChain!==undefined ? NewChain : Chain, Val1, Val2)); }

    if(typeof(Val1)!==typeof(Val2))
        return DoThrow('Type Mismatch');
    if(Val1===null || Val1===undefined)
        return Val1!==Val2 ? DoThrow('Null/undefined mismatch') : true;
    if(Val1.constructor!==Val2.constructor)
        return DoThrow('Constructor mismatch');
    switch(typeof(Val1))
    {
        case 'object':
            for(var m in Val1)
            {
                if(!Val1.hasOwnProperty(m))
                    continue;
                var CurChain=Chain.concat([m]);
                if(!Val2.hasOwnProperty(m))
                    return DoThrow('Val2 missing property', CurChain);
                DOC_Val(Val1[m], Val2[m], CurChain);
            }
            return true;
        case 'number':
            if(Number.isNaN(Val1))
                return !Number.isNaN(Val2) ? DoThrow('NaN mismatch') : true;
        case 'string':
        case 'boolean':
            return Val1!==Val2 ? DoThrow('Value mismatch') : true;
        case 'function':
            if(Val1.prototype!==Val2.prototype)
                return DoThrow('Prototype mismatch');
            if(Val1!==Val2)
                return DoThrow('Function mismatch');
            return true;
        default:
            return DoThrow('Val1 is unknown type');
    }
}
0 голосов
/ 24 ноября 2018

     let user1 = {
        name: "John",
        address: {
            line1: "55 Green Park Road",
            line2: {
              a:[1,2,3]
            } 
        },
        email:null
        }
    
     let user2 = {
        name: "John",
        address: {
            line1: "55 Green Park Road",
            line2: {
              a:[1,2,3]
            } 
        },
        email:null
         }
    
    // Method 1
    
    function isEqual(a, b) {
          return JSON.stringify(a) === JSON.stringify(b);
    }
    
    // Method 2
    
    function isEqual(a, b) {
      // checking type of a And b
      if(typeof a !== 'object' || typeof b !== 'object') {
        return false;
      }
      
      // Both are NULL
      if(!a && !b ) {
         return true;
      } else if(!a || !b) {
         return false;
      }
      
      let keysA = Object.keys(a);
      let keysB = Object.keys(b);
      if(keysA.length !== keysB.length) {
        return false;
      }
      for(let key in a) {
       if(!(key in b)) {
         return false;
       }
        
       if(typeof a[key] === 'object') {
         if(!isEqual(a[key], b[key]))
           {
             return false;
           }
       } else {
         if(a[key] !== b[key]) {
           return false;
         }
       }
      }
      
      return true;
    }



console.log(isEqual(user1,user2));
...