Javascript中比较两个无序对象数组的自定义实现

Posted

技术标签:

【中文标题】Javascript中比较两个无序对象数组的自定义实现【英文标题】:Custom implementation of comparing two non ordered array of object in Javascript 【发布时间】:2019-01-06 12:32:37 【问题描述】:

我想比较两个对象数组,如果它们的元素相同但顺序不同,则它们应该被视为相等。例如[a:1, b:2] and [b:2, a:1]

我正在使用 lodash-v3 的 isEqual,它可以比较两个值,但只有在数组中的顺序相同时才会给出 true,所以我实现了一个递归比较元素的函数。

function deepEqual(data1, data2) 
  data1 = _.cloneDeep(data1);
  data2 = _.cloneDeep(data2);
  function isDeepEqual(val1, val2) 
    if (_.isArray(val1) && _.isArray(val2)) 
      if (val1.length === val2.length) 
        for (let id1 in val1) 
          let hasMatch = false;
          let matchOnIndex = -1;
          for (let id2 in val2) 
            if (isDeepEqual(val1[id1], val2[id2])) 
              hasMatch = true;
              matchOnIndex = id2;
              break;
            
          
          if (hasMatch) 
            val2.splice(matchOnIndex, 1);
           else 
            return false;
          
        
        return true;
       else 
        return false;
      
    

    if (_.isPlainObject(val1) && _.isPlainObject(val2)) 
      if (Object.keys(val1).length === Object.keys(val2).length) 
        for (let temp1 in val1) 
          if (!isDeepEqual(val1[temp1], val2[temp1])) 
            return false;
          
        
        return true;
       else 
        return false;
      
    
    return _.isEqual(val1, val2);
  

  return isDeepEqual(data1, data2);

上述功能有效,但我如何才能提高它的性能? 如果 lodash3 有任何简单的实现也适用于我。

链接到上述函数的fiddle。

编辑

两个对象数组可以嵌套, 例如

[
  a:1,
  b:[
    c: [1, 2]
  ,
  
    d: [3, 4]
  ]
,
  e:1,
  f:[
    g: [5, 6]
  ,
  
    h: [7, 8]
  ]
]

[
  e:1,
  f:[
    h: [8, 7]
  ,
    g: [6, 5]
  ]
,
  a:1,
  b:[
    d: [4, 3]
  ,
    c: [2, 1]
  ]
]

数组也不能有唯一值(因为用户正在创建这个数组)。

正如@Koushik 和@tokland 所建议的那样,_.isEqualWith 可能会实现这一点。不幸的是,它可以从 lodashv4 获得,所以我不能使用它。 这个comment中也提到了类似的解决方案。

非常抱歉没有明确指定示例。小提琴有所有不同类型的案例。

【问题讨论】:

使用计数排序比较数组的内容。这将为您提供 O(n) 而不是您当前拥有的 O(n^2)。 @miradham,你能给我一个简单的例子或算法,说明如何用计数排序比较两个对象数组吗?我认为它只适用于数字数组。 通过计数排序,我的意思是,您可以散列值并检查其数量和存储值一次。但我说它可以提高性能是错误的。在您的示例对象中,具有相同键的对象可以有多个值,因此您必须检查每个值,这无论如何都会给您 O(n^2)。 【参考方案1】:

我想这就是你想要的

var first = [ a: 1 ,  b: 2 ];
        var second = [ b: 2 ,  a: 1 ];
function comparer(otherArray)
  return function(current)
    return otherArray.filter(function(other)
      return other.a == current.a && other.b == current.b
    ).length == 0;
  

var onlyInA = first.filter(comparer(second));
var onlyInB = second.filter(comparer(first));
result = (onlyInA.concat(onlyInB)).length===0;
console.log(result);

【讨论】:

【参考方案2】:

如何简单地检查一个array 中的每个Object 呈现在另一个array 中并且具有相同的length(两个数组)?

let isEqualArray = (arr1, arr2) => (
        arr1 === arr2 ||    
        arr1.length === arr2.length && 
        !arr1.some(a=> !arr2.find(b=>_.isEqual(a,b)))
)

let a1 = [a:1, b:2],
    a2 = [b:2, a:1];
    
let isEqualArray = (arr1, arr2) => (
		arr1 === arr2 ||	
  		arr1.length === arr2.length && 
		!arr1.some(a=> !arr2.find(b=>_.isEqual(a,b)))
)

console.log(isEqualArray(a1,a2));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.10/lodash.min.js"></script>

_.isEqual_.isEqualWith 与定制器一起使用,具体取决于您要如何比较两个对象!

注意:当您的数组中有重复项时,这不会 100% 起作用,但它至少会确认一个数组的所有项都存在于另一个数组中。您可以进行反向检查,或应用 unique 来对重复数组执行此比较。

【讨论】:

【参考方案3】:

函数_.isEqualWith 应该是起点。现在,有很多方法可以实现它。您可以通过为数组中的项目定义排序来使其非常高效。使用id 作为键的示例:

function isEqualConsideringArraysAsSets(obj1, obj2, key) 
  const customizer = (objValue, othValue) => 
    if (_(objValue).isArray() && _(othValue).isArray()) 
      return _.size(objValue) === _.size(othValue) &&
        _.zip(_.sortBy(objValue, key), _.sortBy(othValue, key))
          .every(([val1, val2]) => isEqualConsideringArraysAsSets(val1, val2));
    
  ;

  return _.isEqualWith(obj1, obj2, customizer);


console.log(isEqualConsideringArraysAsSets([id: 1, id: 2], [id: 2, id: 1], "id"))

【讨论】:

_.zip。你错过了.

以上是关于Javascript中比较两个无序对象数组的自定义实现的主要内容,如果未能解决你的问题,请参考以下文章

如何创建在检​​查相等性时忽略某些键的自定义无序映射比较器?

JavaScript对象浅谈

保存不断附加的自定义对象数组

JavaScript数组与对象的关系

比较两个对象数组并获取 Javascript 中的差异

JavaScript如何比较两个数组的内容是否相同