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


751

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

例如:

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

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

9
对称还是非对称?
Lightness Races in Orbit

借助新的ES6功能,可以将其作为一个简单的衬纸即可完成(要在所有主流浏览器中使用都将花费大量时间)。无论如何,请检查一下我的回答
Salvador Dali

1
解决方案的一个重要方面是性能。这种操作的渐近时间复杂性-在其他语言中-是O(a1.length x log(a2.length))-JavaScript可以实现这种性能吗?
罗尔

Answers:


218

我假设您正在比较普通数组。如果不是,则需要将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"));

如果您不关心向后兼容性,那么更好的解决方案是使用过滤器。但是,此解决方案仍然有效。


46
这可能有效,但是它执行三个循环来完成使用Array的filter方法可以在一行代码中完成的工作。
Joshaven Potter,2010年

9
只是要清楚,这实现了对称差A1A2,不像其他的答案张贴在这里。
200_success

25
这不是最好的答案,但是我给它一个慈善的赞成票,以帮助弥补不公正的反对票。只有错误的答案才应该被否决,而且如果我正在从事范围广泛的项目(艰难时期发生),那么这个答案甚至可能会有所帮助。
Michael Scheper

3
我可以知道什么时候会发生什么var a1 = ['a', 'b'];var a2 = ['a', 'b', 'c', 'd', 'b'];它将返回错误的答案,即['c', 'd', 'b']代替['c', 'd']
skbly7

4
最快的方法是最明显的天真解决方案。我在此线程中测试了所有建议的对称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; }
差分

1220

使用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])

3
我更喜欢检查< 0而不是== -1
Vic

1
计算Array差异是所谓的set operation,因为属性查找是Sets 的工作,比indexOf/ 快几个数量级includes。简而言之,您的解决方案效率很低而且很慢。

@ftor但使用Set,值必须唯一,不是吗?
CervEd

1
@LuisSieira我认为,[1,2,3] [2,3,5]鉴于数字是唯一的,这将是行得通的,但是如果您说了[1,1,2,3] [1,2,3,5]并期望[1]您不能使用Set。但是,您的解决方案也不起作用:-//我最终创建了此功能,因为我想不出一种令人满意的方法来更简洁地实现它。如果您对此有任何想法,我很想知道!
CervEd 16'Oct

3
是不是Array.includes()ES7功能,而无需ES6?(1) (2) -并继续使用ES6,您可以使用Array.some()例如 let intersection = aArray.filter(a => bArray.some(b => a === b)),否?
哈里·凯纳嫩(JariKeinänen)'18年

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

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

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

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

注意 indexOf和filter在ie9之前的ie中不可用。


50
唯一不支持过滤器和indexOf的浏览器是IE8。IE9确实支持它们两者。所以没错。
布莱恩·拉森

14
IE7和IE8仍然是(不幸)非常相关的,但是你可以找到填充工具代码在MDN网站两种功能: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/... developer.mozilla.org/en/JavaScript/参考/ Global_Objects /… 通过IE条件和BOOM加载“兼容性”下列出的代码。支持IE7 / 8。
1nfiniti

44
该解决方案的运行时间为O(n ^ 2),线性解决方案的效率会更高。
jholloman 2012年

75
如果您使用这样的函数:[1,2,3].diff([3,4,5])它将返回[1,2]而不是返回,[1,2,4,5]因此它不能解决原始问题中需要注意的问题。
Bugster 2012年

12
@AlinPurcaru古老的浏览器不支持!=错误。考虑到Netscape 2.0,此定义中的大多数JS代码都是“错误的”。这是一个愚蠢的话。
NullUserException 2013年

304

到目前为止,这是使用jQuery精确获得所需结果的最简单方法:

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

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


4
@Batman是的,但前提是他们要引用相同的(对象{a: 1} != {a: 1})(证明
Matmarbon

是否可以将其与保存自定义对象数据的数组一起使用?我实际上以相同的方式尝试过,但没有成功。任何想法都是高度赞赏的。
LetMeCodeYou 2015年

8
这是把戏吗?该文档将此方法视为DOM元素方法的一部分,而不是常规的数组帮助器。因此,它可能现在就以这种方式工作,但可能不会在将来的版本中使用,因为它不打算以这种方式使用。虽然,如果它将正式成为通用数组助手,我会感到高兴。
robsch 2015年

1
@robsch当.not与数组一起使用时,jQuery使用它的内置实用程序.grep()专门用于过滤数组。我看不到这种变化。
superphonic

1
@vsync听起来像是你是一个对称差后
superphonic

158

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]);

4
我认为这是一个很好的解决方案性能,特别是因为lodash和underscore一直在争取最佳实现。另外,它是IE6兼容的。
mahemoff 2012年

4
请注意,此实现不适用于对象数组。有关更多信息,请参见stackoverflow.com/q/8672383/14731
吉利2012

1
正如答案中提到的那样,如果它是同一个对象,则可以使用,但如果两个对象具有相同的属性,则不能使用。我认为这是可以的,因为平等的概念有所不同(例如,在某些应用中它也可能是“ id”属性)。但是,如果您可以将比较测试传递给intersect(),那就太好了。
mahemoff 2013年

为了后代:Lodash现在有_.differenceBy(),它需要回调进行比较;如果要比较的是对象,则可以放一个可以根据需要对其进行比较的函数。
SomeCallMeTim

2
请注意,如果参数顺序颠倒了,它将无法正常工作。例如。_.difference([5,2,10],[1,2,3,4,5]); 无法获得差异
Russj

79

纯JavaScript

对于“差异”有两种可能的解释。我让你选择你想要的那个。说您有:

var a1 = ['a', 'b'     ];
var a2 = [     'b', 'c'];
  1. 如果要获取['a'],请使用以下功能:

    function difference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      return result;
    }
  2. 如果你想获得['a', 'c'](所有元素包含在任一 a1a2,但不能同时-所谓的对称差),使用此功能:

    function symmetricDifference(a1, a2) {
      var result = [];
      for (var i = 0; i < a1.length; i++) {
        if (a2.indexOf(a1[i]) === -1) {
          result.push(a1[i]);
        }
      }
      for (i = 0; i < a2.length; i++) {
        if (a1.indexOf(a2[i]) === -1) {
          result.push(a2[i]);
        }
      }
      return result;
    }

Lodash /下划线

如果使用lodash,则可以使用_.difference(a1, a2)(上述情况1)或_.xor(a1, a2)(情况2)。

如果使用的是Underscore.js,则可以在_.difference(a1, a2)案例1中使用该函数。

ES6集,用于非常大的阵列

上面的代码在所有浏览器上均适用。但是,对于包含约10,000项以上的大型阵列,它变得非常慢,因为它具有O(n²)复杂度。在许多现代浏览器上,我们可以利用ES6 Set对象来加快处理速度。Lodash Set会在可用时自动使用。如果您不使用lodash,请使用以下实现,灵感来自Axel Rauschmayer的博客文章

function difference(a1, a2) {
  var a2Set = new Set(a2);
  return a1.filter(function(x) { return !a2Set.has(x); });
}

function symmetricDifference(a1, a2) {
  return difference(a1, a2).concat(difference(a2, a1));
}

笔记

如果您关心-0,+ 0,NaN或稀疏数组则所有示例的行为可能令人惊讶或不明显。(对于大多数用途,这无关紧要。)


谢谢。你救了我的一天。我不得不比较一个300K的阵列,并且您的“设置”解决方案运行完美。这应该是公认的答案。
justadev

52

要获得对称差异,您需要以两种方式(或在有多个阵列的情况下以各种方式)比较阵列

在此处输入图片说明


ES7(ECMAScript 2016)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => !b.includes(x)),
        ...b.filter(x => !a.includes(x))
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => !unique.includes(x));
    }));
}

ES6(ECMAScript 2015)

// diff between just two arrays:
function arrayDiff(a, b) {
    return [
        ...a.filter(x => b.indexOf(x) === -1),
        ...b.filter(x => a.indexOf(x) === -1)
    ];
}

// diff between multiple arrays:
function arrayDiff(...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter(x => unique.indexOf(x) === -1);
    }));
}

ES5(ECMAScript 5.1)

// diff between just two arrays:
function arrayDiff(a, b) {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var other = i === 1 ? a : b;
        arr.forEach(function(x) {
            if (other.indexOf(x) === -1) {
                diff.push(x);
            }
        });
    })

    return diff;
}

// diff between multiple arrays:
function arrayDiff() {
    var arrays = Array.prototype.slice.call(arguments);
    var diff = [];

    arrays.forEach(function(arr, i) {
        var others = arrays.slice(0);
        others.splice(i, 1);
        var otherValues = Array.prototype.concat.apply([], others);
        var unique = otherValues.filter(function (x, j) { 
            return otherValues.indexOf(x) === j; 
        });
        diff = diff.concat(arr.filter(x => unique.indexOf(x) === -1));
    });
    return diff;
}

例:

// diff between two arrays:
const a = ['a', 'd', 'e'];
const b = ['a', 'b', 'c', 'd'];
arrayDiff(a, b); // (3) ["e", "b", "c"]

// diff between multiple arrays
const a = ['b', 'c', 'd', 'e', 'g'];
const b = ['a', 'b'];
const c = ['a', 'e', 'f'];
arrayDiff(a, b, c); // (4) ["c", "d", "g", "f"]

对象数组之间的差异

function arrayDiffByKey(key, ...arrays) {
    return [].concat(...arrays.map( (arr, i) => {
        const others = arrays.slice(0);
        others.splice(i, 1);
        const unique = [...new Set([].concat(...others))];
        return arr.filter( x =>
            !unique.some(y => x[key] === y[key])
        );
    }));
}

例:

const a = [{k:1}, {k:2}, {k:3}];
const b = [{k:1}, {k:4}, {k:5}, {k:6}];
const c = [{k:3}, {k:5}, {k:7}];
arrayDiffByKey('k', a, b, c); // (4) [{k:2}, {k:4}, {k:6}, {k:7}]

49

以下解决方案是ES6中一种更清洁的方法。

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

区别

a2.filter(d => !a1.includes(d)) // gives ["c", "d"]

路口

a2.filter(d => a1.includes(d)) // gives ["a", "b"]

析取联合(对称差)

[ ...a2.filter(d => !a1.includes(d)),
  ...a1.filter(d => !a2.includes(d)) ]

它仅在一个方向上起作用。现在想象一下a1 = ['a', 'b', 'e']e将不会被提取。
imrok

是的,这就是集合论差异的原理。(a2 -a1)您正在寻找的是(a2-a1)+(a1-a2)
ifelse.codes

1
@imrok我相信这就是您正在寻找的[... a2.filter(d =>!a1.includes(d)),...(a1.filter(d =>!a2.includes(d)) )]
ifelse.codes

2
漂亮的解决方案,谢谢!
Thiago Alves

40

在这种情况下,您可以使用Set。针对此类操作(联合,相交,差)进行了优化。

一旦不允许重复,请确保适用于您的情况。

var a = new JS.Set([1,2,3,4,5,6,7,8,9]);
var b = new JS.Set([2,4,6,8]);

a.difference(b)
// -> Set{1,3,5,7,9}

4
看起来像一个不错的图书馆!您不能只下载Set功能而不必获得其他所有功能真是太可惜了
Blixt

@Blixt我相信您可以下载所有内容,仅包含set.js文件
Samuel Carrijo,2009年



32
function diff(a1, a2) {
  return a1.concat(a2).filter(function(val, index, arr){
    return arr.indexOf(val) === arr.lastIndexOf(val);
  });
}

合并两个数组,唯一值将仅出现一次,因此indexOf()将与lastIndexOf()相同。


3
我同意这是最干净,最简单的方法,而且很好,它不需要接触原型。“如果您不能向六岁的孩子解释它,那么您自己就不会理解它。” ―艾伯特·爱因斯坦
lacostenycoder 2015年

18

要从另一个数组中减去一个数组,只需使用以下代码段:

var a1 = ['1','2','3','4','6'];
var a2 = ['3','4','5'];

var items = new Array();

items = jQuery.grep(a1,function (item) {
    return jQuery.inArray(item, a2) < 0;
});

它将返回['1,'2','6'],它们是第一个数组中不存在于第二个数组中的项。

因此,根据您的问题样本,以下代码是准确的解决方案:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var _array = new Array();

_array = jQuery.grep(array2, function (item) {
     return jQuery.inArray(item, array1) < 0;
});

14

随着带有set和splat运算符的ES6的到来(在仅在Firefox中可用时,请检查兼容性表),您可以编写以下一行:

var a = ['a', 'b', 'c', 'd'];
var b = ['a', 'b'];
var b1 = new Set(b);
var difference = [...new Set([...a].filter(x => !b1.has(x)))];

这将导致[ "c", "d" ]


只是好奇这和做有什么不同b.filter(x => !a.indexOf(x)))
-chovy

1
@chovy时间复杂度不同。我的解决方案是O(n + m)您的解决方案是O(n * m)n和m是数组的长度。列出长长的清单,我的解决方案将在几秒钟内运行,而您的解决方案将花费数小时。
萨尔瓦多·达利

比较对象列表的属性呢?使用此解决方案可能吗?
chovy

2
a.filter(x => !b1.has(x))更简单。请注意,规范仅要求复杂度平均n * f(m) + mf(m)亚线性。它比n * m(但不一定)更好n + m
Oriol

2
@SalvadorDali var difference = [...new Set([...a].filter(x => !b1.has(x)))];为什么要创建重复的'a'数组?为什么要将过滤器的结果转换为集合,然后又转换为数组?这不是等同于var difference = a.filter(x => !b1.has(x));
Deepak Mittal '18

13

ES2015的功能性方法

计算difference两个数组之间的和是Set操作之一。该术语已经表明Set应该使用本机类型,以提高查找速度。无论如何,当您计算两组之间的差异时,存在三个排列:

[+left difference] [-intersection] [-right difference]
[-left difference] [-intersection] [+right difference]
[+left difference] [-intersection] [+right difference]

这是一个反映这些排列的功能解决方案。

difference

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( differencel(xs) (ys) );

difference

differencer是微不足道的。它只是differencel带有翻转的参数。为了方便起见,您可以编写一个函数:const differencer = flip(differencel)。就这样!

对称的difference

现在我们有了左右一个,实现对称也difference变得很简单:

// small, reusable auxiliary functions

const apply = f => x => f(x);
const flip = f => y => x => f(x) (y);
const concat = y => xs => xs.concat(y);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// left difference

const differencel = xs => ys => {
  const zs = createSet(ys);
  return filter(x => zs.has(x)
     ? false
     : true
  ) (xs);
};


// symmetric difference

const difference = ys => xs =>
 concat(differencel(xs) (ys)) (flip(differencel) (xs) (ys));

// mock data

const xs = [1,2,2,3,4,5];
const ys = [0,1,2,3,3,3,6,7,8,9];


// run the computation

console.log( difference(xs) (ys) );

我想这个例子是一个很好的起点,可以使人对函数式编程的含义有一个印象:

使用可以以许多不同方式插入的构件块进行编程。


12

indexOf()对于小型阵列,使用的解决方案是可以的,但是随着阵列长度的增加,算法的性能也会越来越高O(n^2)。这是一个解决方案,通过使用对象作为关联数组将数组项存储为键,将对大型数组有更好的性能。它还自动消除重复项,但仅适用于字符串值(或可以安全存储为字符串的值):

function arrayDiff(a1, a2) {
  var o1={}, o2={}, diff=[], i, len, k;
  for (i=0, len=a1.length; i<len; i++) { o1[a1[i]] = true; }
  for (i=0, len=a2.length; i<len; i++) { o2[a2[i]] = true; }
  for (k in o1) { if (!(k in o2)) { diff.push(k); } }
  for (k in o2) { if (!(k in o1)) { diff.push(k); } }
  return diff;
}

var a1 = ['a', 'b'];
var a2 = ['a', 'b', 'c', 'd'];
arrayDiff(a1, a2); // => ['c', 'd']
arrayDiff(a2, a1); // => ['c', 'd']

每当对对象执行“ for in”操作时,都想使用Object.hasOwnProperty()。否则,您将冒着循环遍历默认对象原型中添加的每个字段的风险。(或者只是对象的父对象)您还只需要两个循环,一个循环用于创建哈希表,另一个循环在该哈希表上查找。
jholloman 2012年

1
@jholloman我谨表示不同意。现在我们可以控制任何属性的可枚举性,大概您应该包括在枚举期间获得的任何属性。
Phrogz

1
@Phrogz如果您只担心现代浏览器,那么这是一个好方法。不幸的是,我必须在工作中支持IE7,因此石器时代是我的默认思路,我们不倾向于使用垫片。
jholloman

10

Joshaven Potter的上述回答很棒。但是它返回的数组B中的元素不在数组C中,反之亦然。例如,如果var a=[1,2,3,4,5,6].diff( [3,4,5,7]);这样,它将输出:==> [1,2,6],但不会 输出[1,2,6,7],这是两者之间的实际差异。您仍然可以使用上面的Potter的代码,但是也可以一次向后重做比较:

Array.prototype.diff = function(a) {
    return this.filter(function(i) {return !(a.indexOf(i) > -1);});
};

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

var a=[1,2,3,4,5,6].diff( [3,4,5,7]);
var b=[3,4,5,7].diff([1,2,3,4,5,6]);
var c=a.concat(b);
console.log(c);

这应该输出: [ 1, 2, 6, 7 ]


9

解决问题的另一种方法

function diffArray(arr1, arr2) {
    return arr1.concat(arr2).filter(function (val) {
        if (!(arr1.includes(val) && arr2.includes(val)))
            return val;
    });
}

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

另外,您可以使用箭头函数语法:

const diffArray = (arr1, arr2) => arr1.concat(arr2)
    .filter(val => !(arr1.includes(val) && arr2.includes(val)));

diffArray([1, 2, 3, 7], [3, 2, 1, 4, 5]);    // return [7, 4, 5]

7
Array.prototype.difference = function(e) {
    return this.filter(function(i) {return e.indexOf(i) < 0;});
};

eg:- 

[1,2,3,4,5,6,7].difference( [3,4,5] );  
 => [1, 2, 6 , 7]

您永远不应以这种方式扩展本机对象。如果该标准difference在将来的版本中作为功能引入,并且此功能具有不同于您的功能签名,则它将破坏您的代码或使用该功能的外部库。
t.niese

7

具有JavaScript过滤功能的非常简单的解决方案:

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

function diffArray(arr1, arr2) {
  var newArr = [];
  var myArr = arr1.concat(arr2);
  
    newArr = myArr.filter(function(item){
      return arr2.indexOf(item) < 0 || arr1.indexOf(item) < 0;
    });
   alert(newArr);
}

diffArray(a1, a2);


6

这个怎么样:

Array.prototype.contains = function(needle){
  for (var i=0; i<this.length; i++)
    if (this[i] == needle) return true;

  return false;
} 

Array.prototype.diff = function(compare) {
    return this.filter(function(elem) {return !compare.contains(elem);})
}

var a = new Array(1,4,7, 9);
var b = new Array(4, 8, 7);
alert(a.diff(b));

因此,您可以通过这种方式array1.diff(array2)来获得它们的差异(尽管我相信算法的时间复杂度很差-我相信O(array1.length x array2.length))


使用filter选项是一个好主意……但是,您不需要为Array创建一个contains方法。我把你的想法变成了一个班轮……谢谢你的启发!
Joshaven Potter,2010年

您不需要定义contains()函数。JS include()做同样的事情。
戴曼

4

使用http://phrogz.net/JS/ArraySetMath.js,您可以:

var array1 = ["test1", "test2","test3", "test4"];
var array2 = ["test1", "test2","test3","test4", "test5", "test6"];

var array3 = array2.subtract( array1 );
// ["test5", "test6"]

var array4 = array1.exclusion( array2 );
// ["test5", "test6"]

4
function diffArray(arr1, arr2) {
  var newArr = arr1.concat(arr2);
  return newArr.filter(function(i){
    return newArr.indexOf(i) == newArr.lastIndexOf(i);
  });
}

这对我有用


3
  • 纯JavaScript解决方案(无库)
  • 与旧版浏览器兼容(不使用filter
  • O(n ^ 2)
  • 可选的fn回调参数,可让您指定如何比较数组项

function diff(a, b, fn){
    var max = Math.max(a.length, b.length);
        d = [];
    fn = typeof fn === 'function' ? fn : false
    for(var i=0; i < max; i++){
        var ac = i < a.length ? a[i] : undefined
            bc = i < b.length ? b[i] : undefined;
        for(var k=0; k < max; k++){
            ac = ac === undefined || (k < b.length && (fn ? fn(ac, b[k]) : ac == b[k])) ? undefined : ac;
            bc = bc === undefined || (k < a.length && (fn ? fn(bc, a[k]) : bc == a[k])) ? undefined : bc;
            if(ac == undefined && bc == undefined) break;
        }
        ac !== undefined && d.push(ac);
        bc !== undefined && d.push(bc);
    }
    return d;
}

alert(
    "Test 1: " + 
    diff(
        [1, 2, 3, 4],
        [1, 4, 5, 6, 7]
      ).join(', ') +
    "\nTest 2: " +
    diff(
        [{id:'a',toString:function(){return this.id}},{id:'b',toString:function(){return this.id}},{id:'c',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        [{id:'a',toString:function(){return this.id}},{id:'e',toString:function(){return this.id}},{id:'f',toString:function(){return this.id}},{id:'d',toString:function(){return this.id}}],
        function(a, b){ return a.id == b.id; }
    ).join(', ')
);


您可以缓存长度值以提高速度。我想建议在不检查长度的情况下访问数组元素,但是显然,简单的检查可以使速度提高近100倍。
Slotos

没有理由缓存length值。它已经是普通财产了。jsperf.com/array-length-caching
vp_arth

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

// 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

对称线性复杂度。需要ES6。

function arrDiff(arr1, arr2) {
    var arrays = [arr1, arr2].sort((a, b) => a.length - b.length);
    var smallSet = new Set(arrays[0]);

    return arrays[1].filter(x => !smallSet.has(x));
}

3

还有另一个答案,但是似乎没人提到jsperf,他们在其中比较了几种算法和技术支持:https ://jsperf.com/array-difference-javascript 似乎使用过滤器可获得最佳结果。谢谢


2

只是想着...为了一个挑战;-)会起作用...(对于字符串,数字等的基本数组)没有嵌套数组

function diffArrays(arr1, arr2, returnUnion){
  var ret = [];
  var test = {};
  var bigArray, smallArray, key;
  if(arr1.length >= arr2.length){
    bigArray = arr1;
    smallArray = arr2;
  } else {
    bigArray = arr2;
    smallArray = arr1;
  }
  for(var i=0;i<bigArray.length;i++){
    key = bigArray[i];
    test[key] = true;
  }
  if(!returnUnion){
    //diffing
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = null;
      }
    }
  } else {
    //union
    for(var i=0;i<smallArray.length;i++){
      key = smallArray[i];
      if(!test[key]){
        test[key] = true;
      }
    }
  }
  for(var i in test){
    ret.push(i);
  }
  return ret;
}

array1 = "test1", "test2","test3", "test4", "test7"
array2 = "test1", "test2","test3","test4", "test5", "test6"
diffArray = diffArrays(array1, array2);
//returns ["test5","test6","test7"]

diffArray = diffArrays(array1, array2, true);
//returns ["test1", "test2","test3","test4", "test5", "test6","test7"]

请注意,排序可能不会如上所述...但是如果需要,请在数组上调用.sort()对其进行排序。


2

我想要一个类似的函数,它接受一个旧数组和一个新数组,并给我一个添加项数组和一个删除项数组,并且我希望它高效(因此没有.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}; 
}

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

对于我的代码,我也需要取出重复项,但是我猜这并不总是首选。

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


2

littlebit修复最佳答案

function arr_diff(a1, a2)
{
  var a=[], diff=[];
  for(var i=0;i<a1.length;i++)
    a[a1[i]]=a1[i];
  for(var i=0;i<a2.length;i++)
    if(a[a2[i]]) delete a[a2[i]];
    else a[a2[i]]=a2[i];
  for(var k in a)
   diff.push(a[k]);
  return diff;
}

这将考虑当前元素的类型。b / c当我们制作a [a1 [i]]时,它将其原始值转换为字符串值,因此我们丢失了实际值。


对于对象数组,这仍然失败。var a = [{{a:1}],b = [{{b:2}] arr_diff(a,b)== []。
EricP
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.