如何在JavaScript中合并两个数组并去重

1962

我有两个 JavaScript 数组:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

我希望输出结果是:

var array3 = ["Vijendra","Singh","Shakya"];

输出数组应该删除重复的单词。

我如何在JavaScript中合并两个数组,以便获取每个数组中唯一的项,并以它们插入原始数组的相同顺序返回?


32
在发布新答案之前,请考虑此问题已经有75个以上的答案。请确保您的答案提供的信息不在现有答案中。 - janniks
5
结果为 [1, 2, 3, 4],这行代码使用了 ES6 的 Set 数据结构和展开运算符将两个数组去重合并。 - Denis Giffeler
如果您想要一个更通用的解决方案,也包括深度合并,请查看这个问题。一些答案也涵盖了数组。 - Martin Braun
简而言之 - 合并数组 (ba) : a=a.concat(b); 从数组 a 中删除重复项 (就地操作) : a=a.filter((i,p)=>a.indexOf(i)===p); - ashleedawg
如果你不想再有更多答案,可以关闭问题。 - Janos Vinceller
问题仍然开放,等待不同、创新、前沿的答案。这也是免责声明的原因。 - Rodrigo Rodrigues
92个回答

10

您可以使用新的Set对象来去除重复项。

[...new Set([...array1 ,...array2])]

请注意,以下是关于编程的内容。保留原文顺序。 - msteel9999
适用于字符串数组的绝佳解决方案,但不适用于对象数组。 - askepott

10

合并两个数组的解决方案很多。它们可以分为两大类(除了使用第三方库如lodash或underscore.js)。

a) 合并两个数组并删除重复项。

b) 在合并之前过滤掉某些项。

合并两个数组并删除重复项

合并

// mutable operation(array1 is the combined array)
array1.push(...array2);
array1.unshift(...array2);

// immutable operation
const combined = array1.concat(array2);
const combined = [...array1, ...array2];    // ES6

统一化

有许多方法可以将数组进行统一化,个人建议以下两种方法。

// a little bit tricky
const merged = combined.filter((item, index) => combined.indexOf(item) === index);
const merged = [...new Set(combined)];

在组合项目之前过滤它们

有很多方法,但我个人建议使用以下代码,因为它简单易懂。

const merged = array1.concat(array2.filter(secItem => !array1.includes(secItem)));

9
您可以简单地使用Underscore.js的uniq实现此目标:
array3 = _.uniq(array1.concat(array2))

console.log(array3)

它将打印["Vijendra", "Singh", "Shakya"]


8

新方案(使用 Array.prototype.indexOfArray.prototype.concat):

Array.prototype.uniqueMerge = function( a ) {
    for ( var nonDuplicates = [], i = 0, l = a.length; i<l; ++i ) {
        if ( this.indexOf( a[i] ) === -1 ) {
            nonDuplicates.push( a[i] );
        }
    }
    return this.concat( nonDuplicates )
};

使用方法:

>>> ['Vijendra', 'Singh'].uniqueMerge(['Singh', 'Shakya'])
["Vijendra", "Singh", "Shakya"]

Array.prototype.indexOf(适用于Internet Explorer):

Array.prototype.indexOf = Array.prototype.indexOf || function(elt)
  {
    var len = this.length >>> 0;

    var from = Number(arguments[1]) || 0;
    from = (from < 0) ? Math.ceil(from): Math.floor(from); 
    if (from < 0)from += len;

    for (; from < len; from++)
    {
      if (from in this && this[from] === elt)return from;
    }
    return -1;
  };

@Mender:如果顺序不重要,那我该怎么做呢? - Vijjendra
1
虽然我知道你可以轻松地为IE和其他不支持它的浏览器定义它,但这不是Array.prototype定义的标准ECMAScript方法。 - meder omuraliev
请注意,此算法的时间复杂度为O(n^2)。 - Gumbo
你的答案用了什么算法? - meder omuraliev
@meder:我的算法是一个并集算法。并集本身的复杂度为O(n+m),但排序最多需要O(n·log n+m·log m)。因此整个算法的复杂度为O(n·log n+m·log m)。 - Gumbo
显示剩余8条评论

8
可以使用 Set 完成此操作。

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var array3 = array1.concat(array2);
var tempSet = new Set(array3);
array3 = Array.from(tempSet);

//show output
document.body.querySelector("div").innerHTML = JSON.stringify(array3);
<div style="width:100%;height:4rem;line-height:4rem;background-color:steelblue;color:#DDD;text-align:center;font-family:Calibri" > 
  temp text 
</div>


7
//Array.indexOf was introduced in javascript 1.6 (ECMA-262) 
//We need to implement it explicitly for other browsers, 
if (!Array.prototype.indexOf)
{
  Array.prototype.indexOf = function(elt, from)
  {
    var len = this.length >>> 0;

    for (; from < len; from++)
    {
      if (from in this &&
          this[from] === elt)
        return from;
    }
    return -1;
  };
}
//now, on to the problem

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];

var merged = array1.concat(array2);
var t;
for(i = 0; i < merged.length; i++)
  if((t = merged.indexOf(i + 1, merged[i])) != -1)
  {
    merged.splice(t, 1);
    i--;//in case of multiple occurrences
  }

indexOf 方法在其他浏览器中的实现来自 MDC


1
我在w3schools上找不到,所以我自己写了。http://www.w3schools.com/jsref/jsref_obj_array.asp 顺便问一下,它需要一个“from”参数吗? - Amarghosh
感谢@Gumbo和@meder - 我现在要更改我的书签。我还没有在js中做过任何严肃的事情,我只是用w3schools进行休闲参考(这就是我所需要的全部)-也许这就是为什么我没有意识到这一点的原因。 - Amarghosh
MDC表示indexOf需要javascript 1.6。可以安全地假设常见的浏览器(>= FF2,> IE6等)会支持它吗? - Amarghosh
4
IE6不支持Array.prototype.indexOf方法,可以直接使用Mozilla提供的支持方法来避免在IE中报错。 - meder omuraliev
使用 indexOf 进行更新。通过删除注释部分来清理代码。@meder - 再次感谢。 - Amarghosh

7
const array3 = array1.filter(t=> !array2.includes(t)).concat(array2)

2
请阅读如何撰写好的答案?。虽然这个代码块可能回答了OP的问题,但如果您解释一下这段代码与问题中的代码有何不同,您做了哪些更改,为什么要更改以及为什么这样解决问题而不会引入其他问题,那么这个答案将会更加有用。 - Saeed Zhiany
这个问题已经存在了将近13年,已经有超过100个答案,其中包括一个得分超过2,000的被接受的答案。你确定这个答案还没有被提供过吗?如果是,请编辑它以解释它如何改进已经存在的内容。 - Chris
它是最佳答案,已经有一个赞了。 - Jar
1
对我来说,这似乎是一个相当不错的答案。我很高兴世界不像SO那样有太多规则,我懒得去学。 - msteel9999

6
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

这个方法不错的地方在于它的性能,通常在处理数组时,我们会使用像filter、map等方法的链式调用,因此您可以添加这一行代码,它将会将array2与array1连接并去重,而无需对后者进行引用(当您进行链式调用时,您没有对后者的引用),例如:
someSource()
.reduce(...)
.filter(...)
.map(...) 
// and now you want to concat array2 and deduplicate:
.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)
// and keep chaining stuff
.map(...)
.find(...)
// etc

我不喜欢污染Array.prototype,这将是尊重链的唯一方式 - 定义新函数会破坏它 - 所以我认为像这样做是实现这一目标的唯一途径。

6
Array.prototype.add = function(b){
    var a = this.concat();                // clone current object
    if(!b.push || !b.length) return a;    // if b is not an array, or empty, then return a unchanged
    if(!a.length) return b.concat();      // if original is empty, return b

    // go through all the elements of b
    for(var i = 0; i < b.length; i++){
        // if b's value is not in a, then add it
        if(a.indexOf(b[i]) == -1) a.push(b[i]);
    }
    return a;
}

// Example:
console.log([1,2,3].add([3, 4, 5])); // will output [1, 2, 3, 4, 5]

5

消除单个数组的重复项或合并和消除多个数组输入的重复项。以下是示例。

使用ES6-Set,for of,解构

我编写了这个简单的函数,它接受多个数组参数。 与上面的解决方案几乎相同,只是具有更实际的用例。此函数仅不将重复值连接到一个数组中,以便稍后可以将它们删除。

简短的函数定义(仅9行)

/**
* This function merging only arrays unique values. It does not merges arrays in to array with duplicate values at any stage.
*
* @params ...args Function accept multiple array input (merges them to single array with no duplicates)
* it also can be used to filter duplicates in single array
*/
function arrayDeDuplicate(...args){
   let set = new Set(); // init Set object (available as of ES6)
   for(let arr of args){ // for of loops through values
      arr.map((value) => { // map adds each value to Set object
         set.add(value); // set.add method adds only unique values
      });
   }
   return [...set]; // destructuring set object back to array object
   // alternativly we culd use:  return Array.from(set);
}

使用示例CODEPEN

// SCENARIO 
let a = [1,2,3,4,5,6];
let b = [4,5,6,7,8,9,10,10,10];
let c = [43,23,1,2,3];
let d = ['a','b','c','d'];
let e = ['b','c','d','e'];

// USEAGE
let uniqueArrayAll = arrayDeDuplicate(a, b, c, d, e);
let uniqueArraySingle = arrayDeDuplicate(b);

// OUTPUT
console.log(uniqueArrayAll); // [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 43, 23, "a", "b", "c", "d", "e"]
console.log(uniqueArraySingle); // [4, 5, 6, 7, 8, 9, 10]

为什么在这里使用 arr.map?你将其用作 foreach,因为结果被忽略了。 - Antony
1
我使用了 __return Array.from(set.values());__,因为 vscode 对 return [...set]; 报错。 - makkasi

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