95

我想比较 JavaScript 代码中的 2 个对象数组。对象总共有 8 个属性,但每个对象都不会有一个值,并且数组永远不会大于 8 个项目,所以可能是遍历每个对象然后查看值的蛮力方法8 个属性是做我想做的最简单的方法,但在实施之前,我想看看是否有人有更优雅的解决方案。有什么想法吗?

4

16 回答 16

65

由于序列化通常不起作用(仅当属性的顺序匹配时:)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
于 2019-03-20T08:22:46.640 回答
64

编辑:您不能在当前、常见的基于浏览器的 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;
}

假设两个对象具有相同的确切属性列表。

哦,很明显,无论好坏,我属于唯一一个返回点阵营。:)

于 2008-08-26T01:39:34.340 回答
21

老实说,每个对象最多有 8 个对象和 8 个属性,最好的办法是遍历每个对象并直接进行比较。它会很快而且很容易。

如果您要经常使用这些类型的比较,那么我同意 Jason 关于 JSON 序列化的观点……但否则没有必要使用新库或 JSON 序列化代码来减慢您的应用程序的速度。

于 2008-08-26T12:52:12.320 回答
20

我知道这是一个老问题,提供的答案工作正常......但这有点短,不需要任何额外的库(即 JSON ):

function arraysAreEqual(ary1,ary2){
  return (ary1.join('') == ary2.join(''));
}
于 2011-05-20T15:23:01.883 回答
17

我研究了一个简单的算法来比较两个对象的内容并返回一个可理解的差异列表。以为我会分享。它借鉴了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;
}
于 2010-09-19T18:32:41.177 回答
14

我尝试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
于 2018-10-16T19:38:09.077 回答
3

这是我的尝试,使用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);
    });
}
于 2017-03-20T12:48:15.520 回答
3

当函数需要等于空数组时(在这种情况下返回 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;
}
于 2021-02-11T00:39:41.517 回答
2

我在排序、测试和工作方面的实践实施。

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 ) );
于 2021-05-11T15:33:27.717 回答
1

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

于 2009-03-16T06:40:43.713 回答
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;
  }
}
于 2014-08-11T06:51:41.780 回答
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);
于 2019-01-03T23:24:46.673 回答
0

与json相比非常糟糕。试试这个包来比较嵌套数组并获得差异。

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

于 2021-01-30T08:42:55.003 回答
0

这是我的解决方案。它将比较也有对象和数组的数组。元素可以停留在任何位置。例子:

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));
于 2021-02-14T19:07:45.373 回答
0

我正在分享我的比较函数实现,因为它可能对其他人有帮助:

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

于 2021-11-09T10:01:13.187 回答
0

不确定性能...必须在大对象上进行测试..但是,这对我很有用..与其他解决方案相比,它的优势是对象/数组不必按相同的顺序排列……

它实际上获取第一个数组中的第一个对象,并扫描第二个数组中的每个对象..如果匹配,它将继续到另一个

绝对有一种优化方法,但它正在工作:)

谢谢@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));
};
于 2021-12-09T08:14:03.447 回答