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



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Обновлено: вы не можете перегрузить операторы в текущих распространенных реализациях интерпретаторов JavaScript на основе браузера.
Чтобы ответить на исходный вопрос, вы можете сделать это одним из способов, и имейте в виду, это немного похоже на взлом, просто сериализовать два массива в JSON и затем сравните две строки JSON. Это просто скажет вам, отличаются ли массивы, очевидно, вы можете сделать это с каждый объектов в массивах, чтобы увидеть, какие из них были разными.
Другой вариант - использовать библиотеку, в которой есть хорошие возможности для сравнения объектов - я использую и рекомендую MochiKit.
Обновлено:Каменс дал ответ также заслуживает внимания, поскольку одна функция для сравнения двух заданных объектов была бы намного меньше, чем любая библиотека, выполняющая то, что я предлагаю (хотя мое предложение, безусловно, сработает достаточно хорошо).
Вот наивная реализация, которой может быть достаточно для вас - знайте, что с этой реализацией есть потенциальные проблемы:
function objectsAreSame(x, y) {
var objectsAreSame = true;
for(var propertyName in x) {
if (x[propertyName] !== y[propertyName]) {
objectsAreSame = false;
break;
}
}
return objectsAreSame;
}
Предполагается, что оба объекта имеют одинаковый точный список свойств.
О, и, вероятно, очевидно, что, к лучшему или худшему, я принадлежу к лагерю с единственной точкой возврата. :)
Одно предостережение относительно предложения сериализации JSON заключается в том, что если вы сравниваете объекты (не массивы) и не заботитесь о порядке (например, именованные ключи, а не числовой массив), тогда сериализация JSON не будет работать.
@AlanH. Вы имеете в виду, что JSON.stringify должен работать для сравнения двух массивов, не являющихся объектами (например, Number, String), а также двух массивов объектов, но не для сравнения двух объектов? Если да, то почему? особенно в случае сравнения двух массивов объектов?
Забудьте о массивах объектов и массивах объектов, что бы это ни значило. Подумайте только о {a: 1, b: 1} и {b: 1, a: 1}. Нас не волнует порядок объектов, но эти строки явно разные.
Честно говоря, с максимум 8 объектами и максимум 8 свойствами для каждого объекта лучше всего просто пройти по каждому объекту и провести сравнение напрямую. Это будет быстро и легко.
Если вы собираетесь часто использовать эти типы сравнений, то я согласен с Джейсоном в отношении сериализации JSON ... но в противном случае нет необходимости замедлять работу вашего приложения с помощью новой библиотеки или кода сериализации JSON.
«... Я согласен с Джейсоном насчет JSON ...» +1 только за это! ;-)
Функция objectsAreSame, упомянутая в ответе @ JasonBunting, у меня отлично работает. Однако есть небольшая проблема: если x[propertyName] и y[propertyName] являются объектами (typeof x[propertyName] == 'object'), вам нужно будет вызвать функцию рекурсивно, чтобы сравнить их.
Я немного поработал над простым алгоритмом для сравнения содержимого двух объектов и получения внятного списка различий. Думал, что поделюсь. Он заимствует некоторые идеи для jQuery, а именно реализацию функции map и проверку типов объектов и массивов.
Он возвращает список «объектов различий», которые представляют собой массивы с информацией о различиях. Все очень просто.
Вот:
// 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's 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's 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;
}
Я знаю, что это старый вопрос, и предоставленные ответы работают нормально ... но он немного короче и не требует дополнительных библиотек (например, JSON):
function arraysAreEqual(ary1,ary2){
return (ary1.join('') == ary2.join(''));
}
ОП хотел объединить массивы объектов. Это работает только для массивов скаляров.
Еще он хрупкий. Если: a=["1,2"] , b=["1", "2"], то join() на двух разных массивах приведет к '1,2'
@ Джейсон Мур не соответствует действительности, a.join ('') // => "1,2"; b.join ('') // => "12"
Вы правы насчет этого конкретного примера, но он все еще хрупкий. a=["12"], b=["1", "2"] ведет к "12"= = "12", и я не думаю, что какой-либо разделитель может вас спасти, потому что он может быть в самом obj. И проверка длины не может исправить это, потому что a=["12", "3"], b=["1", "23"]
Чуть более надежная реализация: return ary1.join(',') === ary2.join(',');
@BriceRoncace, если бы вы удосужились прочитать три предыдущих комментария, вы бы увидели, что ваше решение столь же хрупкое / бессмысленное, как и решение в ответе.
Я не понимаю, почему этот ответ имеет даже 20 голосов - он даже не отвечает на вопрос.
Примерьте вот это:
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;
}
}
ммм ... оператор if можно упростить до return c.length === a.length;
Вот моя попытка с использованием пакета Модуль утверждения узла + npm объект-хеш.
Я предполагаю, что вы хотите проверить, содержат ли два массива одни и те же объекты, даже если эти объекты упорядочены по-разному в двух массивах.
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.info("PASS");
} catch (e) {
console.info("FAIL");
console.info(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);
});
}
Я пробовал JSON.stringify() и работал у меня.
let array1 = [1,2,{value:'alpha'}] , array2 = [{value:'alpha'},'music',3,4];
JSON.stringify(array1) // "[1,2,{"value":"alpha"}]"
JSON.stringify(array2) // "[{"value":"alpha"},"music",3,4]"
JSON.stringify(array1) === JSON.stringify(array2); // false
Обратите внимание - это не сработает, если свойства объекта не в порядке.
сначала мы можем отсортировать массив, а затем использовать stringify
@Ehsansarshar, это не сработает ... вам нужно отсортировать все свойства и массивы объектов ...
используя _.some из lodash: https://lodash.com/docs/4.17.11#some
const array1AndArray2NotEqual =
_.some(array1, (a1, idx) => a1.key1 !== array2[idx].key1
|| a1.key2 !== array2[idx].key2
|| a1.key3 !== array2[idx].key3);
Поскольку сериализация обычно не работает (только если порядок свойств совпадает: JSON.stringify({a:1,b:2}) !== JSON.stringify({b:2,a:1})), вам необходимо проверить количество свойств и также сравнить каждое свойство:
const objectsEqual = (o1, o2) =>
Object.keys(o1).length === Object.keys(o2).length
&& Object.keys(o1).every(p => o1[p] === o2[p]);
const obj1 = { name: 'John', age: 33};
const obj2 = { age: 33, name: 'John' };
const obj3 = { name: 'John', age: 45 };
console.info(objectsEqual(obj1, obj2)); // true
console.info(objectsEqual(obj1, obj3)); // falseЕсли вам нужно глубокое сравнение, вы можете вызвать функцию рекурсивно:
const obj1 = { name: 'John', age: 33, info: { married: true, hobbies: ['sport', 'art'] } };
const obj2 = { age: 33, name: 'John', info: { hobbies: ['sport', 'art'], married: true } };
const obj3 = { name: 'John', age: 33 };
const objectsEqual = (o1, o2) =>
typeof o1 === 'object' && Object.keys(o1).length > 0
? Object.keys(o1).length === Object.keys(o2).length
&& Object.keys(o1).every(p => objectsEqual(o1[p], o2[p]))
: o1 === o2;
console.info(objectsEqual(obj1, obj2)); // true
console.info(objectsEqual(obj1, obj3)); // falseТогда эту функцию легко использовать для сравнения объектов в массивах:
const arr1 = [obj1, obj1];
const arr2 = [obj1, obj2];
const arr3 = [obj1, obj3];
const arraysEqual = (a1, a2) =>
a1.length === a2.length && a1.every((o, idx) => objectsEqual(o, a2[idx]));
console.info(arraysEqual(arr1, arr2)); // true
console.info(arraysEqual(arr1, arr3)); // false
Лучший ответ. Лаконично и безупречно. Должно быть наверху.
Большой. Просто завершите глубокое сравнение проверкой для работы с нулевым свойством или верните o1 === o2. Круто.
сравнение с json довольно плохо. попробуйте этот пакет, чтобы сравнить вложенные массивы и увидеть разницу.
Есть оптимизированный код для случая, когда функция должна равняться пустым массивам (и возвращать false в этом случае)
const objectsEqual = (o1, o2) => {
if (o2 === null && o1 !== null) return false;
return o1 !== null && typeof o1 === 'object' && Object.keys(o1).length > 0 ?
Object.keys(o1).length === Object.keys(o2).length &&
Object.keys(o1).every(p => objectsEqual(o1[p], o2[p]))
: (o1 !== null && Array.isArray(o1) && Array.isArray(o2) && !o1.length &&
!o2.length) ? true : o1 === o2;
}
Вот мое решение. Он будет сравнивать массивы, которые также имеют объекты и массивы. Элементы можно размещать в любых положениях. Пример:
const array1 = [{a: 1}, {b: 2}, { c: 0, d: { e: 1, f: 2, } }, [1,2,3,54]];
const array2 = [{a: 1}, {b: 2}, { c: 0, d: { e: 1, f: 2, } }, [1,2,3,54]];
const arraysCompare = (a1, a2) => {
if (a1.length !== a2.length) return false;
const objectIteration = (object) => {
const result = [];
const objectReduce = (obj) => {
for (let i in obj) {
if (typeof obj[i] !== 'object') {
result.push(`${i}${obj[i]}`);
} else {
objectReduce(obj[i]);
}
}
};
objectReduce(object);
return result;
};
const reduceArray1 = a1.map(item => {
if (typeof item !== 'object') return item;
return objectIteration(item).join('');
});
const reduceArray2 = a2.map(item => {
if (typeof item !== 'object') return item;
return objectIteration(item).join('');
});
const compare = reduceArray1.map(item => reduceArray2.includes(item));
return compare.reduce((acc, item) => acc + Number(item)) === a1.length;
};
console.info(arraysCompare(array1, array2));
Просто чтобы указать на ограничение: похоже, это не сработает при сравнении объектов, содержащих объекты. (И, как вы упомянули, он потерпит неудачу, если два объекта не имеют «одного и того же точного списка свойств», поскольку
yможет быть надмножествомx.