Подтвердить что ты не робот

Глубокое сравнение объектов/массивов

Возможный дубликат:
Как вы определяете равенство для двух объектов JavaScript?
Сравнение объектов в JavaScript

Если у меня есть два массива или объекты и вы хотите их сравнить, например

object1 = [
 { shoes:
   [ 'loafer', 'penny' ]
  },
  { beers:
     [ 'budweiser', 'busch' ]
  }
]

object2 = [
 { shoes:
   [ 'loafer', 'penny' ]
  },
  { beers:
     [ 'budweiser', 'busch' ]
  }
]

object1 == object2 // false

Это может раздражать, если вы получаете ответ от сервера и пытаетесь увидеть, изменилось ли оно

4b9b3361

Ответ 1

Обновление:
В ответ на комментарии и опасения, связанные с первоначальным предложением (сравнение 2 строк JSON), вы можете использовать эту функцию:

function compareObjects(o, p)
{
    var i,
        keysO = Object.keys(o).sort(),
        keysP = Object.keys(p).sort();
    if (keysO.length !== keysP.length)
        return false;//not the same nr of keys
    if (keysO.join('') !== keysP.join(''))
        return false;//different keys
    for (i=0;i<keysO.length;++i)
    {
        if (o[keysO[i]] instanceof Array)
        {
            if (!(p[keysO[i]] instanceof Array))
                return false;
            //if (compareObjects(o[keysO[i]], p[keysO[i]] === false) return false
            //would work, too, and perhaps is a better fit, still, this is easy, too
            if (p[keysO[i]].sort().join('') !== o[keysO[i]].sort().join(''))
                return false;
        }
        else if (o[keysO[i]] instanceof Date)
        {
            if (!(p[keysO[i]] instanceof Date))
                return false;
            if ((''+o[keysO[i]]) !== (''+p[keysO[i]]))
                return false;
        }
        else if (o[keysO[i]] instanceof Function)
        {
            if (!(p[keysO[i]] instanceof Function))
                return false;
            //ignore functions, or check them regardless?
        }
        else if (o[keysO[i]] instanceof Object)
        {
            if (!(p[keysO[i]] instanceof Object))
                return false;
            if (o[keysO[i]] === o)
            {//self reference?
                if (p[keysO[i]] !== p)
                    return false;
            }
            else if (compareObjects(o[keysO[i]], p[keysO[i]]) === false)
                return false;//WARNING: does not deal with circular refs other than ^^
        }
        if (o[keysO[i]] !== p[keysO[i]])//change !== to != for loose comparison
            return false;//not the same value
    }
    return true;
}

Но во многих случаях это не должно быть сложной ИМО:

JSON.stringify(object1) === JSON.stringify(object2);

Если стробированные объекты одинаковы, их значения одинаковы.
Для полноты: JSON просто игнорирует функции (ну, удаляет их все вместе). Это означает представление данных, а не функциональность.
Попытка сравнить 2 объекта, которые содержат только функции, приведет к true:

JSON.stringify({foo: function(){return 1;}}) === JSON.stringify({foo: function(){ return -1;}});
//evaulutes to:
'{}' === '{}'
//is true, of course

Для глубокого сравнения объектов/функций вам придется обратиться к libs или написать свою собственную функцию и преодолеть тот факт, что объекты JS - это все ссылки, поэтому при сравнении o1 === ob2 он возвращает только true, если обе переменные указывают на один и тот же объект...

Как отметил @a-j в комментарии:

JSON.stringify({a: 1, b: 2}) === JSON.stringify({b: 2, a: 1});

- false, так как обе строковые вызовы дают "{"a":1,"b":2}" и "{"b":2,"a":1}" соответственно. Что касается этого, вам нужно понять внутренности двигателя хрома V8. Я не эксперт, и не вдаваясь в подробности, вот что это сводится к:

Каждый объект, который создается, и каждый раз, когда он модифицируется, V8 создает новый скрытый класс С++ (сорт). Если объект X имеет свойство a, а другой объект имеет то же свойство, оба этих объекта JS будут ссылаться на скрытый класс, который наследуется от общего скрытого класса, который определяет это свойство a. Если два объекта имеют одни и те же базовые свойства, то все они будут ссылаться на одни и те же скрытые классы, а JSON.stringify будет работать одинаково на обоих объектах. Это заданное (подробнее об объектах V8 здесь, если вам интересно).

Однако в примере, указанном a-j, оба объекта строятся по-разному. Как так? Ну, проще говоря, эти объекты никогда не существуют одновременно:

JSON.stringify({a: 1, b: 2})

Это вызов функции, выражение, которое необходимо разрешить для результирующего значения, прежде чем его можно сравнить с правым операндом. Второй литерал объекта еще не находится на столе.
Объект стробируется, и exoression разрешается к строковой константе. Литерал объекта не ссылается нигде и помечен для сбора мусора.
После этого правый операнд (выражение JSON.stringify({b: 2, a: 1})) получает такое же лечение.

Все прекрасное и денди, но то, что также нужно учитывать, это то, что JS-двигатели теперь намного сложнее, чем раньше. Опять же, я не эксперт V8, но я думаю, что его правдоподобно, что a-j snippet сильно оптимизирован, поскольку код оптимизирован для:

"{"b":2,"a":1}" === "{"a":1,"b":2}"

По сути опуская вызов JSON.stringify, и просто добавляя кавычки в нужные места. То есть, в конце концов, намного эффективнее.

Ответ 2

В качестве подчеркивания:

в кофе script:

_.mixin deepEquals: (ar1, ar2) ->

    # typeofs should match
    return false unless (_.isArray(ar1) and _.isArray(ar2)) or (_.isObject(ar1) and _.isObject(ar2))

    #lengths should match
    return false if ar1.length != ar2.length

    still_matches = true

    _fail = -> still_matches = false

    _.each ar1, (prop1, n) =>

      prop2 = ar2[n]

      return if prop1 == prop2

      _fail() unless _.deepEquals prop1, prop2

    return still_matches

И в javascript:

_.mixin({
  deepEquals: function(ar1, ar2) {
    var still_matches, _fail,
      _this = this;
    if (!((_.isArray(ar1) && _.isArray(ar2)) || (_.isObject(ar1) && _.isObject(ar2)))) {
      return false;
    }
    if (ar1.length !== ar2.length) {
      return false;
    }
    still_matches = true;
    _fail = function() {
      still_matches = false;
    };
    _.each(ar1, function(prop1, n) {
      var prop2;
      prop2 = ar2[n];
      if (prop1 !== prop2 && !_.deepEquals(prop1, prop2)) {
        _fail();
      }
    });
    return still_matches;
  }
});