我想在JavaScript代码中比较两个对象数组。这些对象共有8个属性,但每个对象可能不会为所有属性赋值,并且这两个数组的长度都小于等于8。因此,也许暴力方法(遍历每个对象并查看8个属性的值)是实现我的目标最简单的方式,但在实施之前,我想知道是否有更简洁的解决方案。 有什么想法吗?
我想在JavaScript代码中比较两个对象数组。这些对象共有8个属性,但每个对象可能不会为所有属性赋值,并且这两个数组的长度都小于等于8。因此,也许暴力方法(遍历每个对象并查看8个属性的值)是实现我的目标最简单的方式,但在实施之前,我想知道是否有更简洁的解决方案。 有什么想法吗?
由于序列化通常不起作用(仅当属性的顺序匹配时才有效: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.log(objectsEqual(obj1, obj2)); // true
console.log(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.log(objectsEqual(obj1, obj2)); // true
console.log(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.log(arraysEqual(arr1, arr2)); // true
console.log(arraysEqual(arr1, arr3)); // false
编辑:在当前常见的基于浏览器的 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;
}
假设两个对象具有完全相同的属性列表。
哦,而且很明显,无论好坏,我属于只有一个返回点的阵营。 :)
y
是x
的超集。 - Alan H.JSON.stringify
既适用于比较两个非Object类型的数组(例如Number、String),也适用于比较两个Object类型的数组,但不适用于比较两个Object对象?如果是这样,为什么?特别是在比较两个Object数组的情况下? - zyxue{a: 1, b: 1}
和{b: 1, a: 1}
这两个东西。对于对象,我们不关心它们的顺序,但这两个字符串显然是不同的。 - Alan H.function arraysAreEqual(ary1,ary2){
return (ary1.join('') == ary2.join(''));
}
a=["1,2"],b=["1", "2"]
,那么对这两个不同的数组进行 join()
操作将导致结果为 '1,2'
。 - Jason Moorea=["12"],b=["1", "2"]
会导致"12"=="12"
,我认为任何分隔符都无法解决这个问题,因为它可能存在于对象本身中。也不能通过长度检查来修复它,因为a=["12", "3"],b=["1", "23"]
。 - Qsarioreturn ary1.join(',') === ary2.join(',');
。 - Brice Roncace我曾经研究了一下简单的算法,用于比较两个对象的内容并返回一个易于理解的差异列表。考虑分享出来。它借鉴了一些jQuery的思想,例如map
函数的实现和对象和数组类型检查。
它返回一个"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'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.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
当函数需要等于空数组时,有一个经过优化的代码(在这种情况下返回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 obj1 = { name: 'John', age: 33};
const obj2 = { age: 33, name: 'John' };
const obj3 = { name: 'John', age: 45 };
const equalObjs = ( obj1, obj2 ) => {
let keyExist = false;
for ( const [key, value] of Object.entries(obj1) ) {
// Search each key in reference object and attach a callback function to
// compare the two object keys
if( Object.keys(obj2).some( ( e ) => e == key ) ) {
keyExist = true;
}
}
return keyExist;
}
console.info( equalObjs( obj1, obj2 ) );
比较您的数组
// Sort Arrays
var arr1 = arr1.sort(( a, b ) => {
var fa = Object.keys(a);
var fb = Object.keys(b);
if (fa < fb) {
return -1;
}
if (fa > fb) {
return 1;
}
return 0;
});
var arr2 = arr2.sort(( a, b ) => {
var fa = Object.keys(a);
var fb = Object.keys(b);
if (fa < fb) {
return -1;
}
if (fa > fb) {
return 1;
}
return 0;
});
const equalArrays = ( arr1, arr2 ) => {
// If the arrays are different length we an eliminate immediately
if( arr1.length !== arr2.length ) {
return false;
} else if ( arr1.every(( obj, index ) => equalObjs( obj, arr2[index] ) ) ) {
return true;
} else {
return false;
}
}
console.info( equalArrays( arr1, arr2 ) );
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);
});
}
关于性能还不确定...需要在大型对象上进行测试...但是对我来说效果很好...与其他解决方案相比的优势是,对象/数组不必按照相同的顺序排列...
它实际上是取第一个数组中的第一个对象,并扫描第二个数组中的每个对象...如果匹配,则会继续进行下一个
虽然有优化的方法,但它已经可以工作了 :)
感谢@ttulka,我受到他的工作启发...只是稍微改进了一下
const objectsEqual = (o1, o2) => {
let match = false
if(typeof o1 === 'object' && Object.keys(o1).length > 0) {
match = (Object.keys(o1).length === Object.keys(o2).length && Object.keys(o1).every(p => objectsEqual(o1[p], o2[p])))
}else {
match = (o1 === o2)
}
return match
}
const arraysEqual = (a1, a2) => {
let finalMatch = []
let itemFound = []
if(a1.length === a2.length) {
finalMatch = []
a1.forEach( i1 => {
itemFound = []
a2.forEach( i2 => {
itemFound.push(objectsEqual(i1, i2))
})
finalMatch.push(itemFound.some( i => i === true))
})
}
return finalMatch.every(i => i === true)
}
const ar1 = [
{ id: 1, name: "Johnny", data: { body: "Some text"}},
{ id: 2, name: "Jimmy"}
]
const ar2 = [
{name: "Jimmy", id: 2},
{name: "Johnny", data: { body: "Some text"}, id: 1}
]
console.log("Match:",arraysEqual(ar1, ar2))
jsfiddle: https://jsfiddle.net/x1pubs6q/
或者只使用lodash :))))
const _ = require('lodash')
const isArrayEqual = (x, y) => {
return _.isEmpty(_.xorWith(x, y, _.isEqual));
};