【问题标题】:How to get the difference between two arrays in JavaScript?如何在 JavaScript 中获取两个数组之间的差异?
【发布时间】:2010-11-14 07:45:50
【问题描述】:

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

例如:

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

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

【问题讨论】:

  • 对称还是非对称?
  • 有了新的 ES6 功能,这可以作为一个简单的单行来完成(要能够在所有主要浏览器中使用需要很多时间)。无论如何检查我的answer
  • 解决方案的一个重要方面是性能。这种操作的渐近时间复杂度——在其他语言中——是O(a1.length x log(a2.length))——这种性能在 JavaScript 中是可能的吗?
  • 检查我的库,它可以帮助你,@netilon/differify 是对象/数组比较最快的差异库之一:npmjs.com/package/@netilon/differify
  • 1.将 a1 转换为集合。 o(a1)。 2. 遍历 e2 以查看 e1 没有的内容。 o(e2)。 3. 将 diff 推入另一个数组,然后在第 2 步完成后将其返回。

标签: javascript arrays array-difference


【解决方案1】:

使用 ES7 有更好的方法:


交叉口

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

对于[1,2,3] [2,3],它将产生[2,3]。另一方面,[1,2,3] [2,3,5] 将返回相同的内容。


区别

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

对于[1,2,3] [2,3],它将产生[1]。另一方面,[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])

【讨论】:

  • 计算Array 的差异是所谓的set operation,因为属性查找是Sets 自己的工作,比indexOf/includes 快几个数量级.简而言之,您的解决方案非常低效且相当缓慢。
  • @ftor 但对于Set,值必须是唯一的,不是吗?
  • @LuisSieira 我知道它适用于[1,2,3] [2,3,5],因为这些数字是唯一的,但如果你说[1,1,2,3] [1,2,3,5] 和预期的[1],你就不能使用Set。但是,您的解决方案也不起作用:-/我最终创建了this function,因为我找不到更简洁的令人满意的方法。如果您对如何做到这一点有任何想法,我很想知道!
  • Array.includes() 不是 ES7 功能而不是 ES6 吗? (1) (2) — 继续,在 ES6 中你可以使用 Array.some() 例如let intersection = aArray.filter(a => bArray.some(b => a === b)),不是吗?
  • @chovy arr1 和 arr2 是您正在执行操作的数组。
【解决方案2】:

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之前不可用。

【讨论】:

  • 唯一不支持 filter 和 indexOf 的浏览器是 IE8。 IE9 确实支持它们。所以没有错。
  • ie7 和 ie8 仍然(不幸地)非常相关,但是您可以在 MDN 网站上找到这两个函数的 polyfill 代码:developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…developer.mozilla.org/en/JavaScript/Reference/Global_Objects/… 通过IE 条件 & 繁荣。支持ie7/8。
  • 此解决方案的运行时间为 O(n^2),线性解决方案效率更高。
  • 如果你使用这样的函数:[1,2,3].diff([3,4,5]),它将返回[1,2]而不是[1,2,4,5],因此它不能解决原始问题中的问题,需要注意。
  • @AlinPurcaru 不受老式浏览器支持!= 错误。考虑到 Netscape 2.0,这里的大部分 JS 代码按照这个定义都是“错误的”。说起来很傻。
【解决方案3】:

这个答案是 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"));

【讨论】:

  • 这可能行得通,但它做了三个循环来完成使用 Array 的 filter 方法在一行代码中可以完成的事情。
  • 为了清楚起见,这实现了 a1a2对称差异,与此处发布的其他答案不同.
  • 这不是最好的答案,但我给它一个慈善投票,以帮助弥补不公平的投票。只有 wrong 答案应该被否决,如果我正在处理一个范围内包含粗略浏览器的项目(艰难时期),这个答案甚至可能会有所帮助。
  • 我想知道var a1 = ['a', 'b'];var a2 = ['a', 'b', 'c', 'd', 'b']; 会发生什么,它会返回错误答案,即['c', 'd', 'b'] 而不是['c', 'd']
  • 最快的方法是最明显幼稚的解决方案。我在这个线程中测试了所有针对对称差异提出的解决方案,获胜者是: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 &lt; la; i++) { if (b.indexOf(a[i]) === -1) res.push(a[i]); } for (i = 0; i &lt; lb; i++) { if (a.indexOf(b[i]) === -1) res.push(b[i]); } return res; }
【解决方案4】:

这是迄今为止使用 jQuery 获得所需结果的最简单方法:

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

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

【讨论】:

  • @Batman 是的,但前提是它们引用了 same 对象 ({a: 1} != {a: 1}) (proof)
  • 是否可以将它与保存自定义对象数据的数组一起使用?我实际上以相同的方式尝试过,但没有奏效。任何想法都会受到高度评价。
  • 这是诡计吗? doc 将此方法视为DOM Element Methods 的一部分,而不是一般的数组助手。所以它现在可能会以这种方式工作,但可能不会在未来的版本中,因为它不打算以这种方式使用它。不过,如果它正式成为一个通用数组助手,我会很高兴。
  • @robsch 当您将.not 与数组一起使用时,jQuery 使用它的内置实用程序.grep(),它专门用于过滤数组。我看不出这种变化。
  • @vsync 听起来你在追求对称差异
【解决方案5】:

The difference method in 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]

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

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

【讨论】:

  • 我认为在性能方面这是一个很好的解决方案,尤其是在 lodash 和 underscore 一直在为最佳实施而战的情况下。此外,它与 IE6 兼容。
  • 请注意,此实现不适用于对象数组。请参阅stackoverflow.com/q/8672383/14731 了解更多信息。
  • 正如其中一个答案提到的那样,如果它是同一个对象,它就可以工作,但如果两个对象具有相同的属性,它就不行。我认为这没关系,因为平等的概念各不相同(例如,它也可能是某些应用程序中的“id”属性)。但是,如果您可以通过对 intersect() 的比较测试,那就太好了。
  • 为了后代:Lodash 现在有 _.differenceBy() ,它需要一个回调来进行比较;如果您正在比较对象,您可以根据需要添加一个比较它们的函数。
  • 要小心,如果参数的顺序颠倒了,它将不起作用。例如。 _.difference([5, 2, 10], [1, 2, 3, 4, 5]);无法获取差异
【解决方案6】:

纯 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 Set,用于非常大的数组

上面的代码适用于所有浏览器。但是,对于超过 10,000 个项目的大型数组,它变得非常慢,因为它具有 O(n²) 复杂度。在许多现代浏览器上,我们可以利用 ES6 Set 对象来加快速度。 Lodash 在可用时自动使用Set。如果您不使用 lodash,请使用以下实现,灵感来自 Axel Rauschmayer's blog post

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 数组,而您的“Set”解决方案效果很好。这应该是公认的答案。
  • 在有人详细使用Set 来解决这个问题之前,我不得不一直向下滚动到这个答案,这真是太棒了。
【解决方案7】:

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 不会被提取。
  • 是的,这就是集合论的不同之处。 (a2 -a1) 你要找的是 (a2-a1) + (a1-a2)
  • @imrok 我相信这就是你要找的 [...a2.filter(d => !a1.includes(d)) , ...(a1.filter(d => !a2.includes(d)))]
【解决方案8】:

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


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

【讨论】:

    【解决方案9】:

    在这种情况下,您可以使用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}
    

    【讨论】:

    【解决方案10】:
    function diff(a1, a2) {
      return a1.concat(a2).filter(function(val, index, arr){
        return arr.indexOf(val) === arr.lastIndexOf(val);
      });
    }
    

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

    【讨论】:

    • 我同意这是最干净、最简单的方法,而且不需要触摸原型。 “如果你不能向一个六岁的孩子解释,你自己就不会理解。” ——阿尔伯特·爱因斯坦
    【解决方案11】:

    随着带有集合和 splat 运算符的 ES6 的到来(当时仅在 Firefox 中有效,请查看compatibility table),您可以编写以下一行:

    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 =&gt; !a.indexOf(x)))有什么不同@
    • @chovy 它的时间复杂度不同。我的解决方案是 O(n + m) 你的解决方案是 O(n * m) 其中 n 和 m 是数组的长度。列出很长的列表,我的解决方案将在几秒钟内运行,而您的解决方案则需要几个小时。
    • 比较对象列表的属性怎么样?使用此解决方案是否可行?
    • a.filter(x =&gt; !b1.has(x)) 更简单。请注意,规范只要求复杂度为n * f(m) + mf(m) 平均为次线性。比n * m好,但不一定是n + m
    • @SalvadorDali var difference = [...new Set([...a].filter(x =&gt; !b1.has(x)))]; 你为什么要创建重复的“a”数组?为什么要将过滤器的结果变成一个集合,然后再变成数组?这不等于var difference = a.filter(x =&gt; !b1.has(x));
    【解决方案12】:

    要从另一个数组中减去一个数组,只需使用下面的 sn-p:

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

    【讨论】:

      【解决方案13】:

      解决问题的另一种方法

      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]
      

      【讨论】:

        【解决方案14】:

        ES2015 的函数式方法

        计算两个数组之间的differenceSet 操作之一。该术语已经表明应使用本机 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) );

        我想这个例子是了解函数式编程意味着什么的一个很好的起点:

        使用可以以多种不同方式组合在一起的构建块进行编程。

        【讨论】:

          【解决方案15】:

          一个衬垫

          const unique = (a) => [...new Set(a)];
          const uniqueBy = (x,f)=>Object.values(x.reduce((a,b)=>((a[f(b)]=b),a),{}));
          const intersection = (a, b) => a.filter((v) => b.includes(v));
          const diff = (a, b) => a.filter((v) => !b.includes(v));
          const symDiff = (a, b) => diff(a, b).concat(diff(b, a));
          const union = (a, b) => diff(a, b).concat(b);
          
          const a = unique([1, 2, 3, 4, 5, 5]);
          console.log(a);
          const b = [4, 5, 6, 7, 8];
          
          console.log(intersection(a, b), diff(a, b), symDiff(a, b), union(a, b));
          
          console.log(uniqueBy(
            [
          { id: 1, name: "abc" },
          { id: 2, name: "xyz" },
          { id: 1, name: "abc" },
            ],
            (v) => v.id
          ));
          
          const intersectionBy = (a, b, f) => a.filter((v) => b.some((u) => f(v, u)));
          
          console.log(intersectionBy(
           [
            { id: 1, name: "abc" },
            { id: 2, name: "xyz" },
           ],
           [
            { id: 1, name: "abc" },
            { id: 3, name: "pqr" },
           ],
           (v, u) => v.id === u.id
          ));
          
          const diffBy = (a, b, f) => a.filter((v) => !b.some((u) => f(v, u)));
          
          console.log(diffBy(
           [
            { id: 1, name: "abc" },
            { id: 2, name: "xyz" },
           ],
           [
            { id: 1, name: "abc" },
            { id: 3, name: "pqr" },
           ],
           (v, u) => v.id === u.id
          ));

          【讨论】:

            【解决方案16】:

            使用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 我respectfully disagree。现在我们可以控制任何属性的可枚举性,大概您应该包括在枚举期间获得的任何属性。
            • @Phrogz 如果您只担心现代浏览器,这很好。不幸的是,我不得不在工作中支持回到 IE7,所以石器时代是我的默认思路,我们不倾向于使用 shim。
            【解决方案17】:

            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 ]

            【讨论】:

              【解决方案18】:

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

              【讨论】:

                【解决方案19】:
                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 作为函数引入,并且该函数具有与您不同的函数签名,它将破坏您的代码或使用该函数的外部库。
                【解决方案20】:

                这个怎么样:

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

                【讨论】:

                • 使用过滤器选项是个好主意...但是,您不需要为 Array 创建 contains 方法。我把你的想法变成了一个班轮......谢谢你的灵感!
                • 你不需要定义 contains() 函数。 JS includes() 做同样的事情。
                【解决方案21】:
                function diffArray(arr1, arr2) {
                  var newArr = arr1.concat(arr2);
                  return newArr.filter(function(i){
                    return newArr.indexOf(i) == newArr.lastIndexOf(i);
                  });
                }
                

                这对我有用

                【讨论】:

                  【解决方案22】:

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

                  【讨论】:

                    【解决方案23】:
                    • 纯 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 倍的加速。
                    • 没有理由缓存length 值。它已经是普通财产。 jsperf.com/array-length-caching
                    【解决方案24】:

                    要找出 2 个不重复的数组的差异

                    function difference(arr1, arr2){
                    
                      let setA = new Set(arr1);
                      let differenceSet = new Set(arr2.filter(ele => !setA.has(ele)));
                      return [...differenceSet ];
                    
                    }
                    

                    1.difference([2,2,3,4],[2,3,3,4]) 将返回[]

                    2.difference([1,2,3],[4,5,6]) 将返回[4,5,6]

                    3.difference([1,2,3,4],[1,2]) 将返回[]

                    4.difference([1,2],[1,2,3,4]) 将返回[3,4]

                    注意:上述解决方案要求您始终将较大的数组作为第二个参数发送。要找到绝对差异,您需要先找到两者中较大的数组,然后对其进行处理。

                    要找出 2 个没有重复的数组的绝对差异

                    function absDifference(arr1, arr2){
                    
                      const {larger, smaller} = arr1.length > arr2.length ? 
                      {larger: arr1, smaller: arr2} : {larger: arr2, smaller: arr1}
                      
                      let setA = new Set(smaller);
                      let absDifferenceSet = new Set(larger.filter(ele => !setA.has(ele)));
                      return [...absDifferenceSet ];
                    
                    }
                    

                    1.absDifference([2,2,3,4],[2,3,3,4]) 将返回[]

                    2.absDifference([1,2,3],[4,5,6]) 将返回[4,5,6]

                    3.absDifference([1,2,3,4],[1,2]) 将返回[3,4]

                    4.absDifference([1,2],[1,2,3,4]) 将返回[3,4]

                    注意两个解决方案中的示例 3

                    【讨论】:

                      【解决方案25】:

                      我想要一个类似的函数,它接收一个旧数组和一个新数组,并给我一个添加项数组和一个删除项数组,我希望它高效(所以没有 .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_ NP!伙计,这已经老了,现在。看到var遍地都是感觉有点?
                      • 大声笑@Ian Grainger 你现在的经历肯定超出了你对昨天的期望......
                      【解决方案26】:

                      你可以使用 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]
                      

                      【讨论】:

                        【解决方案27】:

                        这是可行的:基本上合并两个数组,查找重复项并将未重复的内容推送到一个新数组中,这就是区别。

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

                        【讨论】:

                          【解决方案28】:

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

                          【讨论】:

                            【解决方案29】:

                            对称线性复杂度。需要 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));
                            }
                            

                            【讨论】:

                              【解决方案30】:

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

                              【讨论】:

                                猜你喜欢
                                • 2014-03-26
                                • 1970-01-01
                                相关资源
                                最近更新 更多