比较JavaScript中的对象数组


72

我想比较JavaScript代码中2个对象数组。这些对象共有8个属性,但是每个对象的每个属性都没有值,并且每个数组的大小都不得超过8个,因此遍历每个对象然后查看对象的值的蛮力方法8个属性是执行我想做的最简单的方法,但是在实现之前,我想看看是否有人有一个更优雅的解决方案。有什么想法吗?

Answers:


54

编辑:您不能在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
只是要指出限制:在比较包含对象的对象时,它似乎将失败。(正如你提到的,它会在两个对象没有为失败“属性完全相同的列表,”y允许是超集x
阿伦H.

4
关于JSON序列化建议的一个警告是,如果您比较对象(不是数组)并且不关心顺序(例如,命名键,而不是数字数组),那么JSON序列化将不起作用。
艾伦·H。

@AlanH。您是说JSON.stringify比较两个非对象(例如,数字,字符串)数组和两个对象数组,但不能比较两个对象吗?如果是这样,为什么?尤其是 在比较两个对象数组的情况下?
zyxue

2
不管是什么意思,都不要忘记对象数组和对象数组。想想{a: 1, b: 1}{b: 1, a: 1}。我们不在乎对象的顺序,但是这些字符串明显不同。
艾伦H.17年

30

由于序列化通常不起作用(仅当属性顺序匹配时:),因此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

2
最佳答案。简洁而完美。应该在上面。
IsmailS

大。只需将深度比较与验证进行包装即可使用null属性,否则返回return o1 === o2。凉。
Socrates Tuas

21

我知道这是一个古老的问题,提供的答案很好用...但这有点短,不需要任何其他库(即JSON):

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

14
OP希望加入对象数组。这仅适用于标量数组。
乔纳森·M

10
它也很脆弱。如果:a=["1,2"] , b=["1", "2"]那么join()两个不同数组上的a将导致'1,2'
Jason Moore

@Jason Moore不正确,a.join('')// =>“ 1,2”; b.join('')// =>“ 12”
欧内斯特(Ernest

9
您对那个特定示例是正确的,但它仍然很脆弱。 a=["12"], b=["1", "2"]导致"12"=="12",我认为没有任何定界符可以保存您,因为它可能在obj本身中。长度检查也无法解决,因为a=["12", "3"], b=["1", "23"]
Qsario

5
更为健壮的实施方式:return ary1.join(',') === ary2.join(',');
Brice Roncace 2014年

18

坦白说,最多有8个对象,每个对象最多有8个属性,最好的选择是遍历每个对象并直接进行比较。它会很快并且会很容易。

如果您将经常使用这些类型的比较,那么我同意Jason关于JSON序列化的信息……但是否则,无需使用新的库或JSON序列化代码来减慢您的应用程序的速度。


38
“ ...我同意杰森(Jason)关于JSON ...” +1就是为了这个!;-)
Cerebrus 09-3-16

16

我已经做了一些简单的算法来比较两个对象的内容并返回可理解的差异列表。以为我会分享。它借鉴了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;
}

13

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

17
在这里要注意-如果对象属性
混乱,

首先,我们可以对数组进行排序,然后使用stringify
Ehsan sarshar

@Ehsansarshar无法正常工作...您需要对所有对象的属性和数组进行排序...
Christian

1

请试试这个:

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

1

这是我的尝试,使用Node的assert模块+ 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);
    });
}

1

objectsAreSame@JasonBunting的答案中提到的功能对我来说很好用。但是,有一个小问题:如果x[propertyName]y[propertyName]是对象(typeof x[propertyName] == 'object'),则需要递归调用函数以进行比较。


0

_.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);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.