[英]Comparing Arrays of Objects in JavaScript
我想比較 JavaScript 代碼中的 2 個對象數組。 對象總共有 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;
}
假設兩個對象具有相同的確切屬性列表。
哦,很明顯,無論好壞,我屬於唯一一個返回點陣營。 :)
老實說,每個對象最多有 8 個對象和 8 個屬性,最好的辦法是遍歷每個對象並直接進行比較。 它會很快而且很容易。
如果您要經常使用這些類型的比較,那么我同意 Jason 關於 JSON 序列化的觀點……但否則沒有必要使用新的庫或 JSON 序列化代碼來減慢您的應用程序的速度。
我知道這是一個老問題,提供的答案工作正常......但這有點短,不需要任何額外的庫(即 JSON ):
function arraysAreEqual(ary1,ary2){
return (ary1.join('') == ary2.join(''));
}
我研究了一個簡單的算法來比較兩個對象的內容並返回一個可理解的差異列表。 以為我會分享。 它借鑒了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.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;
}
這是我的嘗試,使用Node 的 assert 模塊+ npm package 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);
});
}
我在排序、測試和工作方面的實踐實施。
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 ) );
請試試這個:
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;
}
}
@JasonBunting 的答案中提到的objectsAreSame
函數對我來說很好。 但是,有一個小問題:如果x[propertyName]
和y[propertyName]
是對象( typeof x[propertyName] == 'object'
),則需要遞歸調用該函數才能比較它們。
我正在分享我的比較函數實現,因為它可能對其他人有幫助:
/* null AND null // true undefined AND undefined // true null AND undefined // false [] AND [] // true [1, 2, 'test'] AND ['test', 2, 1] // true [1, 2, 'test'] AND ['test', 2, 3] // false [undefined, 2, 'test'] AND ['test', 2, 1] // false [undefined, 2, 'test'] AND ['test', 2, undefined] // true [[1, 2], 'test'] AND ['test', [2, 1]] // true [1, 'test'] AND ['test', [2, 1]] // false [[2, 1], 'test'] AND ['test', [2, 1]] // true [[2, 1], 'test'] AND ['test', [2, 3]] // false [[[3, 4], 2], 'test'] AND ['test', [2, [3, 4]]] // true [[[3, 4], 2], 'test'] AND ['test', [2, [5, 4]]] // false [{x: 1, y: 2}, 'test'] AND ['test', {x: 1, y: 2}] // true 1 AND 1 // true {test: 1} AND ['test', 2, 1] // false {test: 1} AND {test: 1} // true {test: 1} AND {test: 2} // false {test: [1, 2]} AND {test: [1, 2]} // true {test: [1, 2]} AND {test: [1]} // false {test: [1, 2], x: 1} AND {test: [1, 2], x: 2} // false {test: [1, { z: 5 }], x: 1} AND {x: 1, test: [1, { z: 5}]} // true {test: [1, { z: 5 }], x: 1} AND {x: 1, test: [1, { z: 6}]} // false */ function is_equal(x, y) { const arr1 = x, arr2 = y, is_objects_equal = function (obj_x, obj_y) { if (!( typeof obj_x === 'object' && Object.keys(obj_x).length > 0 )) return obj_x === obj_y; return Object.keys(obj_x).length === Object.keys(obj_y).length && Object.keys(obj_x).every(p => is_objects_equal(obj_x[p], obj_y[p])); } ; if (!( Array.isArray(arr1) && Array.isArray(arr2) )) return ( arr1 && typeof arr1 === 'object' && arr2 && typeof arr2 === 'object' ) ? is_objects_equal(arr1, arr2) : arr1 === arr2; if (arr1.length !== arr2.length) return false; for (const idx_1 of arr1.keys()) for (const idx_2 of arr2.keys()) if ( ( Array.isArray(arr1[idx_1]) && this.is_equal(arr1[idx_1], arr2[idx_2]) ) || is_objects_equal(arr1[idx_1], arr2[idx_2]) ) { arr2.splice(idx_2, 1); break; } return !arr2.length; }
使用_.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相比非常糟糕。 試試這個包來比較嵌套數組並獲得差異。
這是我的解決方案。 它將比較也有對象和數組的數組。 元素可以停留在任何位置。 例子:
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.log(arraysCompare(array1, array2));
不確定性能...必須在大對象上進行測試..但是,這對我很有用..與其他解決方案相比,它的優勢是對象/數組不必按相同的順序排列……
它實際上獲取第一個數組中的第一個對象,並掃描第二個數組中的每個對象..如果匹配,它將繼續到另一個
絕對有一種優化方法,但它正在工作:)
謝謝@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));
};
這是我在不考慮項目順序的情況下比較兩個對象數組的工作
const collection1 = [ { id: "1", name: "item 1", subtitle: "This is a subtitle", parentId: "1" }, { id: "2", name: "item 2", parentId: "1" }, { id: "3", name: "item 3", parentId: "1" }, ] const collection2 = [ { id: "3", name: "item 3", parentId: "1" }, { id: "2", name: "item 2", parentId: "1" }, { id: "1", name: "item 1", subtitle: "This is a subtitle", parentId: "1" }, ] const contains = (arr, obj) => { let i = arr.length; while (i--) { if (JSON.stringify(arr[i]) === JSON.stringify(obj)) { return true; } } return false; } const isEqual = (obj1, obj2) => { let n = 0 if (obj1.length !== obj2.length) { return false; } for (let i = 0; i < obj1.length; i++) { if (contains(obj2, obj1[i])) { n++ } } return n === obj1.length } console.log(isEqual(collection1,collection2))
如果您考慮到項目的順序,用戶在 lodash isEqual中構建 n 函數
如果你把它們串起來......
type AB = {
nome: string;
}
const a: AB[] = [{ nome: 'Célio' }];
const b: AB[] = [{ nome: 'Célio' }];
console.log(a === b); // false
console.log(JSON.stringify(a) === JSON.stringify(b)); // true
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.