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

IT技术 javascript arrays array-difference
2020-12-16 15:50:18

有没有办法在 JavaScript 中返回两个数组之间的差异?

例如:

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

// need ["c", "d"]
6个回答

使用 ES7 有更好的方法:


路口

 let intersection = arr1.filter(x => arr2.includes(x));

交点差维恩图

因为[1,2,3] [2,3]它会屈服[2,3]另一方面, for[1,2,3] [2,3,5]将返回相同的东西。


区别

let difference = arr1.filter(x => !arr2.includes(x));

右差维恩图

因为[1,2,3] [2,3]它会屈服[1]另一方面, for[1,2,3] [2,3,5]将返回相同的东西。


对于对称差异,您可以执行以下操作:

let difference = arr1
                 .filter(x => !arr2.includes(x))
                 .concat(arr2.filter(x => !arr1.includes(x)));

对称差分维恩图

这样,您将获得一个包含 arr1 中所有不在 arr2 中的元素的数组,反之亦然

正如@Joshaven Potter 在他的回答中指出的那样,您可以将其添加到 Array.prototype 中,这样它就可以像这样使用:

Array.prototype.diff = function(arr2) { return this.filter(x => !arr2.includes(x)); }
[1, 2, 3].diff([2, 3])
@LuisSieira 我知道它会起作用,[1,2,3] [2,3,5]因为这些数字是唯一的,但是如果你说过[1,1,2,3] [1,2,3,5]并期望[1]你不能使用Set. 不过,您的解决方案也行不通:-/ 我最终创建了这个函数,因为我无法找到一种更简洁的令人满意的方法。如果您对如何做到这一点有任何想法,我很想知道!
2021-02-11 15:50:18
@chovy arr1 和 arr2 是您正在执行操作的数组。
2021-02-14 15:50:18
计算Array差异是所谓的set operation,因为属性查找是Sets自己的工作,它比indexOf/快几个数量级includes简而言之,您的解决方案效率很低,而且速度很慢。
2021-02-15 15:50:18
@ftor 但随着Set,值必须是唯一的,不是吗?
2021-02-24 15:50:18
不是Array.includes()ES7 特性而不是 ES6 特性吗?(1) (2) — 继续,在 ES6 中你可以使用Array.some()eg let intersection = aArray.filter(a => bArray.some(b => a === b)),不是吗?
2021-02-25 15:50:18

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return a.indexOf(i) < 0;});
};

//////////////
// Examples //
//////////////

const dif1 = [1,2,3,4,5,6].diff( [3,4,5] );  
console.log(dif1); // => [1, 2, 6]


const dif2 = ["test1", "test2","test3","test4","test5","test6"].diff(["test1","test2","test3","test4"]);  
console.log(dif2); // => ["test5", "test6"]

注意 .indexOf().filter()在 IE9 之前不可用。

ie7 和 ie8 仍然(不幸的是)非常相关,但是您可以在 MDN 站点上找到这两个函数的 polyfill 代码: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/... developer.mozilla.org/en/JavaScript/ Reference/Global_Objects/... 通过 IE 条件和 BOOM 加载“兼容性”下列出的代码。支持 IE7/8。
2021-02-14 15:50:18
该解决方案的运行时间为 O(n^2),线性解决方案效率更高。
2021-02-15 15:50:18
唯一不支持 filter 和 indexOf 的浏览器是 IE8。IE9 确实支持它们。所以没有错。
2021-02-28 15:50:18
如果您像这样使用该函数:[1,2,3].diff([3,4,5])它将返回[1,2]而不是[1,2,4,5]因此它不能解决原始问题中的问题,这是需要注意的。
2021-03-01 15:50:18
@AlinPurcaru 古代浏览器不支持!= 错误。考虑到 Netscape 2.0,根据这个定义,这里的大部分 JS 代码都是“错误的”。说起来很傻。
2021-03-04 15:50:18

这是迄今为止使用 jQuery 准确获得您正在寻找的结果的最简单方法:

var diff = $(old_array).not(new_array).get();

diff现在包含old_array不在的内容new_array

@Batman 是的,但前提是它们是对同一个对象的引用{a: 1} != {a: 1})(证明
2021-02-20 15:50:18
是否可以将它与保存自定义对象数据的数组一起使用?我实际上以相同的方式尝试过,但没有奏效。任何想法都将是非常可观的。
2021-02-23 15:50:18
这是套路吗?文档将此方法视为DOM 元素方法的一部分,而不是一般的数组助手。所以它现在可能会以这种方式工作,但在未来的版本中可能不会,因为它不打算以这种方式使用它。虽然,如果它正式成为通用数组助手,我会很高兴。
2021-02-25 15:50:18
@vsync 听起来你在追求对称差异
2021-03-02 15:50:18
@robsch 当您使用.not数组时,jQuery 使用它的内置实用程序.grep(),该实用程序专门用于过滤数组。我看不到这种变化。
2021-03-09 15:50:18

这个答案是在 2009 年写的,所以有点过时了,而且对于理解这个问题也很有教育意义。我今天使用的最佳解决方案是

let difference = arr1.filter(x => !arr2.includes(x));

(此处感谢其他作者)

我假设您正在比较普通数组。如果不是,则需要将for循环更改for .. in循环。

function arr_diff (a1, a2) {

    var a = [], diff = [];

    for (var i = 0; i < a1.length; i++) {
        a[a1[i]] = true;
    }

    for (var i = 0; i < a2.length; i++) {
        if (a[a2[i]]) {
            delete a[a2[i]];
        } else {
            a[a2[i]] = true;
        }
    }

    for (var k in a) {
        diff.push(k);
    }

    return diff;
}

console.log(arr_diff(['a', 'b'], ['a', 'b', 'c', 'd']));
console.log(arr_diff("abcd", "abcde"));
console.log(arr_diff("zxc", "zxc"));

我可以知道什么时候var a1 = ['a', 'b'];会发生什么var a2 = ['a', 'b', 'c', 'd', 'b'];它会返回错误的答案,即['c', 'd', 'b']而不是['c', 'd'].
2021-02-08 15:50:18
这不是最好的答案,但我给它一个慈善赞成票,以帮助弥补不公平的反对票。只有错误的答案才应该被否决,如果我正在处理一个包含 cruft-browsers 的项目(困难时期发生),这个答案甚至可能会有所帮助。
2021-02-10 15:50:18
这可能有效,但它会执行三个循环来使用 Array 的 filter 方法在一行代码中完成可以完成的工作。
2021-02-24 15:50:18
只是要清楚,这实现了对称差A1A2,不像其他的答案张贴在这里。
2021-02-26 15:50:18
最快的方法是最明显幼稚的解决方案。我在此线程中测试了所有建议的对称差异解决方案,获胜者是:function diff2(a, b) { var i, la = a.length, lb = b.length, res = []; if (!la) return b; else if (!lb) return a; for (i = 0; i < la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i < lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
2021-02-26 15:50:18

Underscore 中的差异方法(或其直接替代品Lo-Dash)也可以做到这一点:

(R)eturns the values from array that are not present in the other arrays

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

与任何 Underscore 函数一样,您也可以以更面向对象的风格使用它:

_([1, 2, 3, 4, 5]).difference([5, 2, 10]);
我认为这是一个很好的性能方面的解决方案,尤其是当 lodash 和 underscore 一直在争取最佳实现时。此外,它与 IE6 兼容。
2021-02-07 15:50:18
请注意,此实现不适用于对象数组。有关更多信息,请参阅stackoverflow.com/q/8672383/14731
2021-02-09 15:50:18
正如那里提到的答案之一,如果它是同一个对象,它就有效,但如果两个对象具有相同的属性,则无效。我认为这没关系,因为平等的概念各不相同(例如,它也可能是某些应用程序中的“id”属性)。但是,如果您可以将比较测试传递给 intersect(),那就太好了。
2021-02-27 15:50:18
如果参数顺序颠倒,请小心,它将不起作用。例如。_.difference([5, 2, 10], [1, 2, 3, 4, 5]); 无法获得差异
2021-02-27 15:50:18
对于后代:Lodash 现在有 _.differenceBy() 它需要一个回调来进行比较;如果您正在比较对象,您可以根据需要放入一个比较它们的函数。
2021-02-28 15:50:18