Сравнение массивов объектов в JavaScript

Я хочу сравнить 2 массива объектов в JavaScript-коде. Объекты имеют 8 общих свойств, но каждый объект не будет иметь значения для каждого, и массивы никогда не будут больше 8 элементов, поэтому, возможно, метод грубой силы пересекает каждый, а затем смотрит на значения 8 свойств - это самый простой способ сделать то, что я хочу сделать, но перед реализацией я хотел увидеть, было ли у кого-то более элегантное решение. Любые мысли?

Ответ 1

РЕДАКТИРОВАТЬ: вы не можете перегружать операторов в текущих, общих браузерах, реализациях интерпретаторов JavaScript.

Чтобы ответить на исходный вопрос, один из способов сделать это, и подумать, это немного взломать, просто сериализовать два массива на JSON, а затем сравнить две строки JSON. Это просто скажет вам, разные ли массивы, очевидно, вы могли бы сделать это для каждого из объектов в массивах, а также посмотреть, какие из них отличаются.

Другой вариант - использовать библиотеку, в которой есть хорошие возможности для сравнения объектов - я использую и рекомендую MochiKit.


РЕДАКТИРОВАТЬ: Ответ, предоставленный kamens, заслуживает внимания, так как единственная функция для сравнения двух заданных объектов будет намного меньше, чем любая библиотека, чтобы делать то, что я предлагаю (хотя мое предложение, безусловно, будет работать достаточно хорошо).

Вот наивная реализация, которая может быть достаточно для вас - помните, что есть потенциальные проблемы с этой реализацией:

function objectsAreSame(x, y) {
   var objectsAreSame = true;
   for(var propertyName in x) {
      if(x[propertyName] !== y[propertyName]) {
         objectsAreSame = false;
         break;
      }
   }
   return objectsAreSame;
}

Предполагается, что оба объекта имеют одинаковый точный список свойств.

О, и, вероятно, очевидно, что, к лучшему или худшему, я принадлежу к лагерю только с одним возвратом.:)

Ответ 2

Я знаю, что это старый вопрос, и предоставленные ответы работают нормально... но это немного короче и не требует каких-либо дополнительных библиотек (например, JSON):

function arraysAreEqual(ary1,ary2){
  return (ary1.join('') == ary2.join(''));
}

Ответ 3

Честно говоря, с 8 объектами max и 8 свойствами max для каждого объекта, лучше всего просто пересечь каждый объект и сделать сравнения напрямую. Это будет быстро, и это будет легко.

Если вы собираетесь часто использовать эти типы сравнений, я согласен с Jason о сериализации JSON... но в противном случае нет необходимости замедлять ваше приложение с помощью новой библиотеки или кода сериализации JSON.

Ответ 4

Я немного поработал над простым алгоритмом, чтобы сравнить содержимое двух объектов и вернуть понятный список различий. Думал, что поделюсь. Он заимствует некоторые идеи для jQuery, а именно реализацию функции карты и проверку типа объекта и массива.

Он возвращает список "объектов diff", которые представляют собой массивы с информацией о diff. Это очень просто.

Вот он:

// compare contents of two objects and return a list of differences
// returns an array where each element is also an array in the form:
// [accessor, diffType, leftValue, rightValue ]
//
// diffType is one of the following:
//   value: when primitive values at that index are different
//   undefined: when values in that index exist in one object but don't in 
//              another; one of the values is always undefined
//   null: when a value in that index is null or undefined; values are
//         expressed as boolean values, indicated wheter they were nulls
//   type: when values in that index are of different types; values are 
//         expressed as types
//   length: when arrays in that index are of different length; values are
//           the lengths of the arrays
//

function DiffObjects(o1, o2) {
    // choose a map() impl.
    // you may use $.map from jQuery if you wish
    var map = Array.prototype.map?
        function(a) { return Array.prototype.map.apply(a, Array.prototype.slice.call(arguments, 1)); } :
        function(a, f) { 
            var ret = new Array(a.length), value;
            for ( var i = 0, length = a.length; i < length; i++ ) 
                ret[i] = f(a[i], i);
            return ret.concat();
        };

    // shorthand for push impl.
    var push = Array.prototype.push;

    // check for null/undefined values
    if ((o1 == null) || (o2 == null)) {
        if (o1 != o2)
            return [["", "null", o1!=null, o2!=null]];

        return undefined; // both null
    }
    // compare types
    if ((o1.constructor != o2.constructor) ||
        (typeof o1 != typeof o2)) {
        return [["", "type", Object.prototype.toString.call(o1), Object.prototype.toString.call(o2) ]]; // different type

    }

    // compare arrays
    if (Object.prototype.toString.call(o1) == "[object Array]") {
        if (o1.length != o2.length) { 
            return [["", "length", o1.length, o2.length]]; // different length
        }
        var diff =[];
        for (var i=0; i<o1.length; i++) {
            // per element nested diff
            var innerDiff = DiffObjects(o1[i], o2[i]);
            if (innerDiff) { // o1[i] != o2[i]
                // merge diff array into parent while including parent object name ([i])
                push.apply(diff, map(innerDiff, function(o, j) { o[0]="[" + i + "]" + o[0]; return o; }));
            }
        }
        // if any differences were found, return them
        if (diff.length)
            return diff;
        // return nothing if arrays equal
        return undefined;
    }

    // compare object trees
    if (Object.prototype.toString.call(o1) == "[object Object]") {
        var diff =[];
        // check all props in o1
        for (var prop in o1) {
            // the double check in o1 is because in V8 objects remember keys set to undefined 
            if ((typeof o2[prop] == "undefined") && (typeof o1[prop] != "undefined")) {
                // prop exists in o1 but not in o2
                diff.push(["[" + prop + "]", "undefined", o1[prop], undefined]); // prop exists in o1 but not in o2

            }
            else {
                // per element nested diff
                var innerDiff = DiffObjects(o1[prop], o2[prop]);
                if (innerDiff) { // o1[prop] != o2[prop]
                    // merge diff array into parent while including parent object name ([prop])
                    push.apply(diff, map(innerDiff, function(o, j) { o[0]="[" + prop + "]" + o[0]; return o; }));
                }

            }
        }
        for (var prop in o2) {
            // the double check in o2 is because in V8 objects remember keys set to undefined 
            if ((typeof o1[prop] == "undefined") && (typeof o2[prop] != "undefined")) {
                // prop exists in o2 but not in o1
                diff.push(["[" + prop + "]", "undefined", undefined, o2[prop]]); // prop exists in o2 but not in o1

            }
        }
        // if any differences were found, return them
        if (diff.length)
            return diff;
        // return nothing if objects equal
        return undefined;
    }
    // if same type and not null or objects or arrays
    // perform primitive value comparison
    if (o1 != o2)
        return [["", "value", o1, o2]];

    // return nothing if values are equal
    return undefined;
}

Ответ 5

Функция objectsAreSame, упомянутая в ответе Джейсона, отлично подходит для меня. Однако есть небольшая проблема: если x[propertyName] и y[propertyName] являются объектами (typeof x[propertyName] == 'object'), вам нужно будет вызвать функцию рекурсивно, чтобы сравнить их.

Ответ 6

Попробуйте следующее:

function used_to_compare_two_arrays(a, b)
{
  // This block will make the array of indexed that array b contains a elements
  var c = a.filter(function(value, index, obj) {
    return b.indexOf(value) > -1;
  });

  // This is used for making comparison that both have same length if no condition go wrong 
  if (c.length !== a.length) {
    return 0;
  } else{
    return 1;
  }
}

Ответ 7

Вот моя попытка, используя Node assert module + пакет npm object-hash.

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

var assert = require('assert');
var hash = require('object-hash');

var obj1 = {a: 1, b: 2, c: 333},
    obj2 = {b: 2, a: 1, c: 444},
    obj3 = {b: "AAA", c: 555},
    obj4 = {c: 555, b: "AAA"};

var array1 = [obj1, obj2, obj3, obj4];
var array2 = [obj3, obj2, obj4, obj1]; // [obj3, obj3, obj2, obj1] should work as well

// calling assert.deepEquals(array1, array2) at this point FAILS (throws an AssertionError)
// even if array1 and array2 contain the same objects in different order,
// because array1[0].c !== array2[0].c

// sort objects in arrays by their hashes, so that if the arrays are identical,
// their objects can be compared in the same order, one by one
var array1 = sortArrayOnHash(array1);
var array2 = sortArrayOnHash(array2);

// then, this should output "PASS"
try {
    assert.deepEqual(array1, array2);
    console.log("PASS");
} catch (e) {
    console.log("FAIL");
    console.log(e);
}

// You could define as well something like Array.prototype.sortOnHash()...
function sortArrayOnHash(array) {
    return array.sort(function(a, b) {
        return hash(a) > hash(b);
    });
}