如何在JavaScript中获取两个数组之间的差异?

1333

有没有一种方法可以在 JavaScript 中返回两个数组的差异?

例如:

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];

// need ["c", "d"]

16
对称还是非对称? - Lightness Races in Orbit
2
使用新的ES6函数,这可以作为一个简单的一行代码完成(在所有主要浏览器中使用它需要很长时间)。无论如何,请查看我的答案 - Salvador Dali
2
解决方案的一个重要方面是性能。这种类型操作的渐进时间复杂度 - 在其他语言中 - 是 O(a1.length x log(a2.length)) - 这种性能在JavaScript中是否可能? - Raul
请查看我的库,它可以帮助您解决这个问题,@netilon/differify 是用于对象/数组比较的最快速的差异库之一: https://www.npmjs.com/package/@netilon/differify - Fabian Orue
1
  1. 将a1转换为集合。o(a1)。
  2. 迭代e2以查看它具有而e1没有的内容。o(e2)。
  3. 将差异推入另一个数组,然后在完成步骤2后返回它。
- powerup7
您可以使用筛选器,检查我的答案https://dev59.com/aXM_5IYBdhLWcg3w3nbz#74317431 - Manu Rastogi
84个回答

3
我想要一个类似的函数,它接收一个旧数组和一个新数组,并给出一个添加项数组和一个删除项数组,而且我希望它高效(不使用.contains!)。
你可以在这里尝试我的解决方案:http://jsbin.com/osewu3/12
有人能看到该算法存在问题或需要改进吗?谢谢!
代码如下:
function diff(o, n) {
  // deal with empty lists
  if (o == undefined) o = [];
  if (n == undefined) n = [];

  // sort both arrays (or this won't work)
  o.sort(); n.sort();

  // don't compare if either list is empty
  if (o.length == 0 || n.length == 0) return {added: n, removed: o};

  // declare temporary variables
  var op = 0; var np = 0;
  var a = []; var r = [];

  // compare arrays and add to add or remove lists
  while (op < o.length && np < n.length) {
      if (o[op] < n[np]) {
          // push to diff?
          r.push(o[op]);
          op++;
      }
      else if (o[op] > n[np]) {
          // push to diff?
          a.push(n[np]);
          np++;
      }
      else {
          op++;np++;
      }
  }

  // add remaining items
  if( np < n.length )
    a = a.concat(n.slice(np, n.length));
  if( op < o.length )
    r = r.concat(o.slice(op, o.length));

  return {added: a, removed: r}; 
}

非常感谢,你的代码帮了我很多!@Ian Grainger - DanyMartinez_
@DanyMartinez_ 没问题!这已经很老了。现在到处都是var,让我感觉有点…… - Ian Grainger
哈哈,@Ian Grainger 毫无疑问,你现在的经验已经超出了你昨天的期望... - DanyMartinez_

3

这是有效的:基本上合并两个数组,寻找重复项,并将未重复的内容推入一个新数组中,这就是差异。

function diff(arr1, arr2) {
  var newArr = [];
  var arr = arr1.concat(arr2);
  
  for (var i in arr){
    var f = arr[i];
    var t = 0;
    for (j=0; j<arr.length; j++){
      if(arr[j] === f){
        t++; 
        }
    }
    if (t === 1){
      newArr.push(f);
        }
  } 
  return newArr;
}


3

3

如果你想找到两个对象数组之间的差异,可以按照以下方式进行:

let arrObj = [{id: 1},{id: 2},{id: 3}]
let arrObj2 = [{id: 1},{id: 3}]

let result = arrObj.filter(x => arrObj2.every(x2 => x2.id !== x.id))

console.log(result)


3

您可以使用underscore.js: http://underscorejs.org/#intersection

您需要的数组方法有:

_.difference([1, 2, 3, 4, 5], [5, 2, 10]);
=> [1, 3, 4]

_.intersection([1, 2, 3], [101, 2, 1, 10], [2, 1]);
=> [1, 2]

3

//ES6方式

function diff(a, b) {
  var u = a.slice(); //dup the array
  b.map(e => {
    if (u.indexOf(e) > -1) delete u[u.indexOf(e)]
    else u.push(e)   //add non existing item to temp array
  })
  return u.filter((x) => {return (x != null)}) //flatten result
}

3

使用额外的内存来做这件事。这样你可以以更少的时间复杂度解决它,O(n)而不是o(n*n)。

function getDiff(arr1,arr2){
let k = {};
let diff = []
arr1.map(i=>{
    if (!k.hasOwnProperty(i)) {
        k[i] = 1
    }
}
)
arr2.map(j=>{
    if (!k.hasOwnProperty(j)) {
        k[j] = 1;
    } else {
        k[j] = 2;
    }
}
)
for (var i in k) {
    if (k[i] === 1)
        diff.push(+i)
}
return diff
}
getDiff([4, 3, 52, 3, 5, 67, 9, 3],[4, 5, 6, 75, 3, 334, 5, 5, 6])

2
function diff(arr1, arr2) {
  var filteredArr1 = arr1.filter(function(ele) {
    return arr2.indexOf(ele) == -1;
  });

  var filteredArr2 = arr2.filter(function(ele) {
    return arr1.indexOf(ele) == -1;
  });
  return filteredArr1.concat(filteredArr2);
}

diff([1, "calf", 3, "piglet"], [1, "calf", 3, 4]); // Log ["piglet",4]

这是对称差异。 - sifar

2

该函数会比较所有值,并返回一个仅包含不重复数值的数组。

var main = [9, '$', 'x', 'r', 3, 'A', '#', 0, 1];

var arr0 = ['Z', 9, 'e', '$', 'r'];
var arr1 = ['x', 'r', 3, 'A', '#'];
var arr2 = ['m', '#', 'a', 0, 'r'];
var arr3 = ['$', 1, 'n', '!', 'A'];


Array.prototype.diff = function(arrays) {
    var items = [].concat.apply(this, arguments);
    var diff = [].slice.call(items), i, l, x, pos;

    // go through all items
    for (x = 0, i = 0, l = items.length; i < l; x = 0, i++) {
        // find all positions
        while ((pos = diff.indexOf(items[i])) > -1) {
            // remove item + increase found count
            diff.splice(pos, 1) && x++;
        }
        // if item was found just once, put it back
        if (x === 1) diff.push(items[i]);
    }
    // get all not duplicated items
    return diff;
};

main.diff(arr0, arr1, arr2, arr3).join(''); // returns "Zeman!"

[].diff(main, arr0, arr1, arr2, arr3).join(''); // returns "Zeman!"

你不应该以这种方式扩展本地对象。如果标准在将来的版本中引入了 diff 作为函数,并且该函数具有与您的函数签名不同的函数签名,则会破坏您的代码或使用此函数的外部库。 - t.niese
@t.niese 嗯,没有人说你必须使用它。如果需要或想要的话,扩展本地对象是很常见的事情,虽然它确实可能会在后面造成问题,但聪明的开发者会很快找到如何使用新版本的方法;) - MarekZeman91
1
@MarekZeman91 嗯,俗话说得好,聪明的开发者可以修复明智的开发者在一开始就知道该避免的问题。 - Guy Passy

2

我在寻找一个简单的答案,不涉及使用不同的库,然后我自己想出了一个方法,我认为这里没有提到过。我不知道它有多高效,但它可以工作;

    function find_diff(arr1, arr2) {
      diff = [];
      joined = arr1.concat(arr2);
      for( i = 0; i <= joined.length; i++ ) {
        current = joined[i];
        if( joined.indexOf(current) == joined.lastIndexOf(current) ) {
          diff.push(current);
        }
      }
      return diff;
    }

对于我的代码,我也需要去除重复项,但我想这并不总是被优先考虑的。

我想主要的缺点就是可能会比较已经被拒绝的许多选项。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接