简体   繁体   English

比较 JavaScript 中的对象数组

[英]Comparing Arrays of Objects in JavaScript

I want to compare 2 arrays of objects in JavaScript code.我想比较 JavaScript 代码中的 2 个对象数组。 The objects have 8 total properties, but each object will not have a value for each, and the arrays are never going to be any larger than 8 items each, so maybe the brute force method of traversing each and then looking at the values of the 8 properties is the easiest way to do what I want to do, but before implementing, I wanted to see if anyone had a more elegant solution.对象总共有 8 个属性,但每个对象都不会有一个值,并且数组永远不会大于 8 个项目,所以可能是遍历每个对象然后查看值的蛮力方法8 个属性是做我想做的最简单的方法,但在实施之前,我想看看是否有人有更优雅的解决方案。 Any thoughts?有什么想法吗?

As serialization doesn't work generally (only when the order of properties matches: JSON.stringify({a:1,b:2}) !== JSON.stringify({b:2,a:1}) ) you have to check the count of properties and compare each property as well:由于序列化通常不起作用(仅当属性的顺序匹配时: 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

If you need a deep comparison, you can call the function recursively:如果需要深度比较,可以递归调用函数:

 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

Then it's easy to use this function to compare objects in arrays:然后很容易使用这个函数来比较数组中的对象:

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

EDIT: You cannot overload operators in current, common browser-based implementations of JavaScript interpreters.编辑:您不能在当前、常见的基于浏览器的 JavaScript 解释器实现中重载运算符。

To answer the original question, one way you could do this, and mind you, this is a bit of a hack, simply serialize the two arrays to JSON and then compare the two JSON strings.要回答原始问题,您可以采用一种方法,请注意,这有点小技巧,只需将两个数组序列化为 JSON ,然后比较两个 JSON 字符串。 That would simply tell you if the arrays are different, obviously you could do this to each of the objects within the arrays as well to see which ones were different.这只会告诉您数组是否不同,显然您也可以对数组中的每个对象执行此操作,以查看哪些不同。

Another option is to use a library which has some nice facilities for comparing objects - I use and recommend MochiKit .另一种选择是使用一个库,它有一些很好的工具来比较对象——我使用并推荐MochiKit


EDIT: The answer kamens gave deserves consideration as well, since a single function to compare two given objects would be much smaller than any library to do what I suggest (although my suggestion would certainly work well enough).编辑: kamens 给出的答案也值得考虑,因为比较两个给定对象的单个函数将比任何库要小得多,以执行我的建议(尽管我的建议肯定会足够好)。

Here is a naïve implemenation that may do just enough for you - be aware that there are potential problems with this implementation:这是一个幼稚的实现,可能对您来说已经足够了 - 请注意此实现存在潜在问题:

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

The assumption is that both objects have the same exact list of properties.假设两个对象具有相同的确切属性列表。

Oh, and it is probably obvious that, for better or worse, I belong to the only-one-return-point camp.哦,很明显,无论好坏,我属于唯一一个返回点阵营。 :) :)

Honestly, with 8 objects max and 8 properties max per object, your best bet is to just traverse each object and make the comparisons directly.老实说,每个对象最多有 8 个对象和 8 个属性,最好的办法是遍历每个对象并直接进行比较。 It'll be fast and it'll be easy.它会很快而且很容易。

If you're going to be using these types of comparisons often, then I agree with Jason about JSON serialization...but otherwise there's no need to slow down your app with a new library or JSON serialization code.如果您要经常使用这些类型的比较,那么我同意 Jason 关于 JSON 序列化的观点……但否则没有必要使用新的库或 JSON 序列化代码来减慢您的应用程序的速度。

I know this is an old question and the answers provided work fine ... but this is a bit shorter and doesn't require any additional libraries ( ie JSON ):我知道这是一个老问题,提供的答案工作正常......但这有点短,不需要任何额外的库(即 JSON ):

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

I have worked a bit on a simple algorithm to compare contents of two objects and return an intelligible list of difference.我研究了一个简单的算法来比较两个对象的内容并返回一个可理解的差异列表。 Thought I would share.以为我会分享。 It borrows some ideas for jQuery, namely the map function implementation and the object and array type checking.它借鉴了jQuery的一些想法,即map函数实现以及对象和数组类型检查。

It returns a list of "diff objects", which are arrays with the diff info.它返回一个“差异对象”列表,这些对象是具有差异信息的数组。 It's very simple.这很简单。

Here it is:这里是:

// 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;
}

I tried JSON.stringify() and worked for me.我尝试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

There is a optimized code for case when function needs to equals to empty arrays (and returning false in that case)当函数需要等于空数组时(在这种情况下返回 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;
}

Here is my attempt, using Node's assert module + npm package object-hash .这是我的尝试,使用Node 的 assert 模块+ npm package object-hash

I suppose that you would like to check if two arrays contain the same objects, even if those objects are ordered differently between the two arrays.我想您想检查两个数组是否包含相同的对象,即使这些对象在两个数组之间的排序不同。

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);
    });
}

My practice implementation with sorting, tested and working.我在排序、测试和工作方面的实践实施。

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 ) );

Compare your arrays比较您的阵列

// 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 ) );

Please try this one:请试试这个:

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;
  }
}

The objectsAreSame function mentioned in @JasonBunting's answer works fine for me. @JasonBunting 的答案中提到的objectsAreSame函数对我来说很好。 However, there's a little problem: If x[propertyName] and y[propertyName] are objects ( typeof x[propertyName] == 'object' ), you'll need to call the function recursively in order to compare them.但是,有一个小问题:如果x[propertyName]y[propertyName]是对象( typeof x[propertyName] == 'object' ),则需要递归调用该函数才能比较它们。

I am sharing my compare function implementation as it might be helpful for others:我正在分享我的比较函数实现,因为它可能对其他人有帮助:

 /* 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; }

using _.some from lodash: https://lodash.com/docs/4.17.11#some使用_.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);

comparing with json is pretty bad.与json相比非常糟糕。 try this package to compare nested arrays and get the difference.试试这个包来比较嵌套数组并获得差异。

https://www.npmjs.com/package/deep-object-diff https://www.npmjs.com/package/deep-object-diff

There`s my solution.这是我的解决方案。 It will compare arrays which also have objects and arrays.它将比较也有对象和数组的数组。 Elements can be stay in any positions.元素可以停留在任何位置。 Example:例子:

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));

not sure about the performance ... will have to test on big objects .. however, this works great for me.. the advantage it has compared to the other solutions is, the objects/array do not have to be in the same order ....不确定性能...必须在大对象上进行测试..但是,这对我很有用..与其他解决方案相比,它的优势是对象/数组不必按相同的顺序排列……

it practically takes the first object in the first array, and scans the second array for every objects .. if it's a match, it will proceed to another它实际上获取第一个数组中的第一个对象,并扫描第二个数组中的每个对象..如果匹配,它将继续到另一个

there is absolutely a way for optimization but it's working :)绝对有一种优化方法,但它正在工作:)

thx to @ttulka I got inspired by his work ... just worked on it a little bit谢谢@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/ jsfiddle: https ://jsfiddle.net/x1pubs6q/

or just use lodash :))))或者只使用 lodash :))))

const _ = require('lodash')

const isArrayEqual = (x, y) => {
  return _.isEmpty(_.xorWith(x, y, _.isEqual));
};

This is work for me to compare two array of objects without taking into consideration the order of the items这是我在不考虑项目顺序的情况下比较两个对象数组的工作

 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))

if you take into consideration the order of the items user build n function in lodash isEqual如果您考虑到项目的顺序,用户在 lodash isEqual中构建 n 函数

If you stringify them...如果你把它们串起来......

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM