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

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

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

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

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

object1 == object2 // false

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

 Juho Vepsäläinen30 окт. 2012 г., 17:09

Ответы на вопрос(2)

Решение Вопроса

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

{
    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, так как оба вызова stringify дают"{"a":1,"b":2}" а также"{"b":2,"a":1}" соответственно. Что касается того, почему это так, вам нужно понять внутренности Chrome V8. Я не эксперт, и не вдаваясь в подробности, вот что сводится к следующему:

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

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

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

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

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

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

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

 Tony Arkles21 сент. 2013 г., 07:00
Я не верю, что у JSON.stringify есть какая-либо гарантия порядка вывода. JSON.stringify ({a: 1, b: 1}) может превратиться в '{"a": 1, "b": 1} "или" {"b": 1, "a": 1}' и сравнение не удастся.
 Elias Van Ootegem18 июл. 2013 г., 16:52
@ansiart: я никогда не утверждал, что это универсальное решение. ОП хотел сравнить два объекта, например, те, что были в его вопросе. Чтобы добиться этого, этот ответ является самым простым способом.
 Elias Van Ootegem25 апр. 2017 г., 11:53
@pomo: не думайте, что это было сделано намеренно (это 4,5-летний ответ). Вероятно, забыл назвать его рекурсивно для элементов массива, или, возможно, что-то было связано с исходным вопросом или комментарием где-то
 ansiart18 июл. 2013 г., 16:40
Это не обязательно правда. Это не охватывает все случаи, то есть функции как значения, объекты как значения - у вас может получиться много [Object Object] - что может быть ложным срабатыванием.
 Elias Van Ootegem22 сент. 2013 г., 14:34
@TonyArkles: порядок, в котором представлены свойства, не определяется ни одним стандартом (ECMA оставляет это до реализации). Однако справедливо сказать, что два объекта с одинаковыми свойствами будут зашифрованы одинаковым образом: оба объекта будут зашифрованы одной и той же реализацией, и их выходные данные будут идентичны, если для обоих объектов будут определены одинаковые свойства. JSON ничего не упорядочивает, но механизм V8, например, упорядочивает свойства в алфавитном порядке. так{b:1,a:1} это невозможность ...

в кофе-скрипте:

_.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;
  }
});
 Funkodebat30 июл. 2013 г., 22:51
да, cofeescript просто возвращает каждую последнюю строку функций
 Sorin Postelnicu29 июл. 2013 г., 16:59
И какова цель возврата из итератора? Как я понял, _.each не прерывает цикл (если это то, что вы хотели) ..
 Sorin Postelnicu29 июл. 2013 г., 17:01
Или это просто полуавтоматическое преобразование из coffeescript, где каждое утверждение имеет возвращаемое значение, поэтому при вызове «_fail (), если только _.deepEquals prop1, prop2» не преобразует это в «return _fail ()»?
 Sorin Postelnicu29 июл. 2013 г., 16:58
Funkodebat, функция итератора, которую вы передали, чтобы подчеркнуть каждую, имеет несовместимые точки возврата: if (prop1 === prop2) {return; / * void * /} if (! _. deepEquals (prop1, prop2)) {return _fail (); /* ложный */ }

Ваш ответ на вопрос