ChatGPT解决这个技术问题 Extra ChatGPT

如何在 JavaScript 中合并两个数组并删除重复项

我有两个 JavaScript 数组:

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

我希望输出为:

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

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

如何在 JavaScript 中合并两个数组,以便按照插入原始数组的相同顺序从每个数组中获取唯一项?

在发布新答案之前,请考虑此问题已有 75 多个答案。请确保您的答案提供的信息不在现有答案中。
[...new Set([...[1, 2, 3], ...[2, 3, 4]])] 结果 [1, 2, 3, 4]
如果您想要一个更通用的解决方案,也涵盖深度合并,请查看 at this question。一些答案也涵盖了数组。
TL;dr - 合并数组ba):a=a.concat(b); 从数组 a 中删除重复项(就地):a=a.filter((i,p)=>a.indexOf(i)===p);
如果您不想要更多答案,请关闭问题。

A
AntoineB

仅合并数组(不删除重复项)

ES5 版本使用 Array.concat:

var array1 = [“维金德拉”,“辛格”]; var array2 = ["Singh", "Shakya"]; array1 = array1.concat(array2);控制台.log(array1);

ES6 版本使用解构

const array1 = ["Vijendra","Singh"];
const array2 = ["Singh", "Shakya"];
const array3 = [...array1, ...array2];

由于没有“内置”方法来删除重复项(ECMA-262 实际上有 Array.forEach,这对这个非常有用),我们必须手动完成:

Array.prototype.unique = function() {
    var a = this.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
};

然后,使用它:

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
// Merges both arrays and gets unique items
var array3 = array1.concat(array2).unique(); 

这也将保留数组的顺序(即,不需要排序)。

由于许多人对 Array.prototypefor in 循环的原型增强感到恼火,因此这里有一种侵入性较小的使用方式:

function arrayUnique(array) {
    var a = array.concat();
    for(var i=0; i<a.length; ++i) {
        for(var j=i+1; j<a.length; ++j) {
            if(a[i] === a[j])
                a.splice(j--, 1);
        }
    }

    return a;
}

var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
    // Merges both arrays and gets unique items
var array3 = arrayUnique(array1.concat(array2));

对于那些有幸使用支持 ES5 的浏览器的人,您可以像这样使用 Object.defineProperty

Object.defineProperty(Array.prototype, 'unique', {
    enumerable: false,
    configurable: false,
    writable: false,
    value: function() {
        var a = this.concat();
        for(var i=0; i<a.length; ++i) {
            for(var j=i+1; j<a.length; ++j) {
                if(a[i] === a[j])
                    a.splice(j--, 1);
            }
        }

        return a;
    }
});

请注意,此算法为 O(n^2)。
[a, b, c][x, b, d] 成为数组(假设引号)。 concat 给出 [a, b, c, x, b, d]。 unique() 的输出不是 [a, c, x, b, d]。这并没有保留我认为的顺序 - 我相信 OP 想要 [a, b, c, x, d]
我最初对此投了赞成票,但后来改变了主意。将原型分配给 Array.prototype 会破坏“for ... in”语句。所以最好的解决方案可能是使用这样的函数但不将其分配为原型。有些人可能会争辩说,无论如何都不应该使用“for ... in”语句来迭代数组元素,但人们经常以这种方式使用它们,因此至少要谨慎使用此解决方案。
您应该始终将 for ... inhasOwnProperty 一起使用,在这种情况下原型方法很好
只需按照另一个答案中的描述使用 Babel 和 Set()
d
dota2pro

使用 Underscore.js 或 Lo-Dash 你可以:

console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));

http://underscorejs.org/#union

http://lodash.com/docs#union


或者,也许比下划线更好的是与 API 兼容的 lodash
@Ygg 来自 lodash 文档。 “按顺序返回一个新的唯一值数组,这些值存在于一个或多个数组中。”
我更喜欢 underscore.js。我最终使用的是 underscore.flatten(),它比 union 更好,因为它需要一个数组数组。
@weaver _.flatten 合并,但不“去重复”。
lodash 与最佳答案的快速性能对比:jsperf.com/merge-two-arrays-keeping-only-unique-values
s
simo

首先连接两个数组,然后只过滤掉唯一的项目:

var a = [1, 2, 3], b = [101, 2, 1, 10] var c = a.concat(b) var d = c.filter((item, pos) => c.indexOf(item ) === pos) console.log(d) // d 是 [1, 2, 3, 101, 10]

编辑

正如所建议的那样,更明智的性能解决方案是在与 a 连接之前过滤掉 b 中的唯一项目:

var a = [1, 2, 3], b = [101, 2, 1, 10] var c = a.concat(b.filter((item) => a.indexOf(item) < 0)) 控制台。 log(c) // c 是 [1, 2, 3, 101, 10]


此处的原始解决方案具有删除每个源数组中的重复项的好处。我想这取决于您将使用的上下文。
您可以合并不同的 IE6 支持: c = Array.from(new Set(c));
如果我想实际更改 a 以添加 b,那么循环并使用推送会更好吗? a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
只是提醒关注 IE6 的人们当前的浏览器使用情况caniuse.com/usage-table
@Andrew:更好:1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
R
Roman Nazarevych
[...array1,...array2] //   =>  don't remove duplication 

或者

[...new Set([...array1 ,...array2])]; //   => remove duplication

第一个/第二个示例根本没有 union + 第一个示例炸毁大型 Array 的堆栈 + 第三个示例非常慢并且消耗大量内存,因为必须构建两个中间 Array + 第三个示例只能用于在编译时具有已知 Array 数量的 union
那么你会怎么做呢?
请注意,for set 不能对具有相同键值对的两个对象进行重复数据删除,除非它们是相同的对象引用。
应该是现代 Javascript 的公认答案。
不适用于对象数组,因为它只会合并对象引用并且不关心对象本身是否相等。
d
dota2pro

这是一个使用 spread operator 和数组泛型的 ECMAScript 6 解决方案。

目前它只适用于 Firefox,可能还有 Internet Explorer Technical Preview。

但如果您使用 Babel,您现在就可以拥有它。

常量输入 = [ [1, 2, 3], [101, 2, 1, 10], [2, 1] ]; const mergeDedupe = (arr) => { return [...new Set([].concat(...arr))]; } console.log('输出',mergeDedupe(输入));


这应该添加到接受的答案中。这个解决方案比目前可能的解决方案更高效、更优雅,但这是我们不可避免地能够做到的(并且应该这样做以跟上这一领域的步伐)。
这与 OP 的问题不同(这似乎更像是一个平面图而不是任何东西),但是因为它太棒了所以要投票。
很难说这应该是公认的答案,因为这个问题是从 2009 年开始的。但是,是的,这不仅更“高性能”而且“优雅”
可以使用 Array.from 代替展开运算符:Array.from(new Set([].concat(...arr)))
这是非常优雅的。不幸的是,Typescript 还不支持这个。 stackoverflow.com/questions/33464504/…
d
dota2pro

使用 Set (ECMAScript 2015),就这么简单:

const array1 = [“维金德拉”,“辛格”]; const array2 = ["Singh", "Shakya"]; console.log(Array.from(new Set(array1.concat(array2))));


我认为这是使用 ES6 的“公认答案”。
@mwieczorek 怎么样:const array3 = [...new Set(array1.concat(array2))]
如果您使用的是对象数组,则它不起作用
用于合并不同对象而不重复:stackoverflow.com/a/54134237/3131433
P
Peter Mortensen

你可以用 ECMAScript 6 简单地做到这一点,

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];

使用扩展运算符连接数组。

使用 Set 创建一组不同的元素。

再次使用扩展运算符将 Set 转换为数组。


我收到错误:类型 'Set' 不是数组类型。
如果您出于某种原因不想使用扩展运算符,还有:Array.from(new Set(array1.concat(array2)))
@gattsbr,使用 tsconfig.json 中的 TypeScript,您可以将 "downlevelIteration": true 添加到 compilerOptions
B
Brak

这是对循环的稍微不同的看法。借助最新版 Chrome 中的一些优化,它是解决两个数组并集的最快方法(Chrome 38.0.2111)。

http://jsperf.com/merge-two-arrays-keeping-only-unique-values

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

var arr = array1.concat(array2),
  len = arr.length;

while (len--) {
  var itm = arr[len];
  if (array3.indexOf(itm) === -1) {
    array3.unshift(itm);
  }
}

while 循环:~589k ops/s 过滤器:~445k ops/s lodash:308k ops/s for 循环:225k ops/s

一条评论指出,我的一个设置变量导致我的循环领先于其他循环,因为它不必初始化要写入的空数组。我同意这一点,所以我重写了测试以适应竞争环境,并包括一个更快的选项。

http://jsperf.com/merge-two-arrays-keeping-only-unique-values/52

let whileLoopAlt = function (array1, array2) {
    const array3 = array1.slice(0);
    let len1 = array1.length;
    let len2 = array2.length;
    const assoc = {};

    while (len1--) {
        assoc[array1[len1]] = null;
    }

    while (len2--) {
        let itm = array2[len2];

        if (assoc[itm] === undefined) { // Eliminate the indexOf call
            array3.push(itm);
            assoc[itm] = null;
        }
    }

    return array3;
};

在这个替代解决方案中,我结合了一个答案的关联数组解决方案来消除循环中的 .indexOf() 调用,这会通过第二个循环大大减慢速度,并包括其他用户在他们的答案也是如此。

每个值(i-1)上的双循环的最佳答案仍然明显慢。 lodash 仍然很强大,我仍然会将它推荐给任何不介意在他们的项目中添加库的人。对于那些不想这样做的人来说,我的 while 循环仍然是一个很好的答案,并且过滤器的答案在这里表现得非常好,在撰写本文时,我使用最新的 Canary Chrome (44.0.2360) 击败了我的所有测试。

如果您想加快速度,请查看 Mike's answerDan Stocker's answer。在经历了几乎所有可行的答案之后,这些是迄今为止最快的结果。


您的方法存在一个缺陷:您将 array3 的创建置于设置阶段,而该成本应该只是您基于 while 的解决方案得分的一部分。 With this 1 line moved,您的解决方案取决于基于 for 循环的解决方案的速度。我知道数组可以重复使用,但也许其他算法也可以从不必声明和初始化每个必要的构建块中受益。
我同意你的前提@doldt,但不同意你的结果。基于循环的条目删除存在一个基本的设计缺陷,即您必须在删除项目后重新检查数组的长度,从而导致执行时间变慢。向后工作的 while 循环没有这些效果。这是一个示例,在不过多更改原始答案的情况下尽可能多地删除设置变量:jsperf.com/merge-two-arrays-keeping-only-unique-values/19
@slickplaid 链接的测试是空的,jsperf 的下一个修订版挂在 while 循环中。
@doldt 我已经在我的回答中解决了您的担忧,并为其添加了适当的更新测试。让我知道您是否同意这些结果。此外,我使用关联数组添加了另一个更好的结果。
@slickplaid 感谢您设置扩展性能页面。除非我遗漏了什么,否则“whileLoopAlt2”功能不起作用?它创建一个包含第一个数组和第二个数组(以相反顺序)的新数组。为避免混淆,我进行了另一个修订,删除了损坏的功能。我还添加了一个附加示例:jsperf.com/merge-two-arrays-keeping-only-unique-values/22
A
Andrew

我简化了 this answer 中最好的部分,并将其变成了一个不错的函数:

function mergeUnique(arr1, arr2){
    return arr1.concat(arr2.filter(function (item) {
        return arr1.indexOf(item) === -1;
    }));
}

我相信这比公认的答案要干净得多。此外,ECMAScript 5.1 + 似乎支持过滤器,现在非常支持。
这要简洁得多。
一个班轮:const mergeUnique = (a, b) => a.concat(b.filter(v => a.indexOf(v) === -1))
这不会从 arr1 中删除 dup,它只会从 arr2 添加唯一元素
B
Bablu Ahmed

ES6 提供了一种单行解决方案,通过使用解构和集合来合并多个数组而不重复。

const array1 = ['a','b','c'];
const array2 = ['c','c','d','e'];
const array3 = [...new Set([...array1,...array2])];
console.log(array3); // ["a", "b", "c", "d", "e"]

这对 2016 年已经提供的相同答案没有任何帮助
k
kabirbaidhya

只需避开嵌套循环 (O(n^2)) 和 .indexOf() (+O(n))。

函数合并(a,b){ var hash = {};变量我; for (i = 0; i < a.length; i++) { hash[a[i]] = true; } for (i = 0; i < b.length; i++) { hash[b[i]] = true; } 返回 Object.keys(hash); } var array1 = ["Vijendra", "Singh"]; var array2 = ["Singh", "Shakya"]; var 数组 3 = 合并(数组 1,数组 2);控制台.log(array3);


这真是太神奇了,尤其是当你在做弦乐的时候。数字将需要一个额外的步骤来保持它们。如果您在完成后不介意(或关心)所有内容都是字符串,那么此功能将大大击败所有其他选项。不错的工作。此处的性能结果:jsperf.com/merge-two-arrays-keeping-only-unique-values/21
M
Mike

只需投入我的两分钱。

function mergeStringArrays(a, b){
    var hash = {};
    var ret = [];

    for(var i=0; i < a.length; i++){
        var e = a[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    for(var i=0; i < b.length; i++){
        var e = b[i];
        if (!hash[e]){
            hash[e] = true;
            ret.push(e);
        }
    }

    return ret;
}

这是我经常使用的一种方法,它使用一个对象作为哈希表来进行重复检查。假设哈希是 O(1),那么它在 O(n) 中运行,其中 n 是 a.length + b.length。老实说,我不知道浏览器如何进行哈希处理,但它在数千个数据点上表现良好。


做得很好。通过利用关联数组并避免 indexOf 和其他操作的循环,在此页面上击败了相当多(如果不是全部)的其他结果。 jsperf.com/merge-two-arrays-keeping-only-unique-values/21
您的“哈希”是 javascript 中的 String() 函数。这可能适用于原始值(尽管类型之间存在冲突),但它不适合对象数组。
我使用类似的解决方案,我允许传递一个 hashCode 函数或传递一个字符串来标识对象中的一个属性以用作哈希键。
G
GAgnew
Array.prototype.merge = function(/* variable number of arrays */){
    for(var i = 0; i < arguments.length; i++){
        var array = arguments[i];
        for(var j = 0; j < array.length; j++){
            if(this.indexOf(array[j]) === -1) {
                this.push(array[j]);
            }
        }
    }
    return this;
};

一个更好的数组合并功能。


var test = ['a', 'b', 'c']; console.log(test); 将打印 ["a", "b", "c", merge: function]
优秀的解决方案。我已经更新了@slickplaid (jsperf.com/merge-two-arrays-keeping-only-unique-values/3) 上面发布的 jsperf 测试,看起来这是其中最快的一个。
@Cobra 冒着听起来小气的风险,在 Chrome 40.0.2214 上运行(截至 2015 年 2 月 18 日最新),这个答案比我的慢 53%。 OTOH IE11 似乎根本没有针对我的答案进行优化。 :) 不过,Chrome 移动版仍在摇摆不定。老实说,如果您使用的是我们大多数人都应该使用的 lodash/_,那么真正的答案已经在这个列表中很高了。 :)
@slickplaid 是的,它的速度要快得多,即使与 lodash/_ 相比也是如此。我可能最终会在某个时间点或另一个将我的实现切换到与您的类似的东西。 :D
不确定 indexOf() 方法的成本是多少,但这可能是最快的 ES5 兼容方法。不需要可变长度的参数也毫无价值。这种方法是可链接的。 @slickplaid 加载库永远不是“如何在 javascript 中执行”问题的答案。当然,许多图书馆都有完成这 7 行工作的功能。
P
Pitouli

编辑:

只有在项目较少时,第一种解决方案才是最快的。当有超过 400 个项目时,Set 解决方案变得最快。当有 100,000 个项目时,它比第一个解决方案快一千倍。

考虑到只有在项目很多时性能才重要,并且 Set 解决方案是迄今为止最具可读性的解决方案,因此在大多数情况下它应该是正确的解决方案

下面的性能结果是用少量项目计算的

基于 jsperf,将两个数组合并到一个新数组中的最快方法(编辑:如果少于 400 项)如下:

for (var i = 0; i < array2.length; i++)
    if (array1.indexOf(array2[i]) === -1)
      array1.push(array2[i]);

这个慢了 17%:

array2.forEach(v => array1.includes(v) ? null : array1.push(v));

这个慢了 45%(编辑:当少于 100 个项目时。当有很多项目时要快很多):

var a = [...new Set([...array1 ,...array2])];

并且接受的答案要慢 55%(并且要写更长的时间)(编辑:当有 100 000 个项目时,它比任何其他方法慢几个数量级)

var a = array1.concat(array2);
for (var i = 0; i < a.length; ++i) {
    for (var j = i + 1; j < a.length; ++j) {
        if (a[i] === a[j])
            a.splice(j--, 1);
    }
}

https://jsperf.com/merge-2-arrays-without-duplicate


感谢这一点,并将性能数字放在易于理解的排名百分比数字中。由于简单,我最初是在搜索基于 Set 的选项。鉴于我的数据集可以变得非常大,性能绝对是一个更重要的考虑因素!
事实证明 Set 快得多,尤其是随着记录的增加(至少对于 Numbers)。请参阅 stackoverflow.com/a/66129415/2578125 上的可运行测试器。
@OXiGEN 是的,Set 的浏览器实现已经改进,或者取决于数据类型。我应该在我的答案中写下我的数组初始化:(
S
Stavm

我知道这个问题与对象数组无关,但搜索者确实会在这里结束。

所以值得为未来的读者添加一个合适的 ES6 合并然后删除重复项的方法

对象数组:

var arr1 = [ {a: 1}, {a: 2}, {a: 3} ];
var arr2 = [ {a: 1}, {a: 2}, {a: 4} ];

var arr3 = arr1.concat(arr2.filter( ({a}) => !arr1.find(f => f.a == a) ));

// [ {a: 1}, {a: 2}, {a: 3}, {a: 4} ]

P
Peter Mortensen

为什么不使用对象?看起来您正在尝试建模一个集合。但是,这不会保留订单。

var set1 = {"Vijendra":true, "Singh":true}
var set2 = {"Singh":true,  "Shakya":true}

// Merge second object into first
function merge(set1, set2){
  for (var key in set2){
    if (set2.hasOwnProperty(key))
      set1[key] = set2[key]
  }
  return set1
}

merge(set1, set2)

// Create set from array
function setify(array){
  var result = {}
  for (var item in array){
    if (array.hasOwnProperty(item))
      result[array[item]] = true
  }
  return result
}

你不是说if (!set1.hasOwnProperty(key))吗?
我为什么要这么说?该条件的目的是忽略对象原型中可能存在的属性。
在每个用例中转换为对象效率不高。例如,我们可能需要来自 Object.keys() 的 2 个数组的键的联合。
K
Kamil Kiełczewski

表现

今天 2020.10.15,我在 Chrome v86、Safari v13.1.2 和 Firefox v81 上对 MacOs HighSierra 10.13.6 进行测试,以选择解决方案。

结果

适用于所有浏览器

解决方案 H 最快/最快

解决方案 L 很快

解决方案 D 在大型阵列的 chrome 上最快

解决方案 G 在小型阵列上速度很快

对于小型阵列,解决方案 M 最慢

解决方案 E 对于大阵列来说是最慢的

https://i.stack.imgur.com/1K2Fn.png

细节

我执行 2 个测试用例:

对于 2 个元素的数组 - 你可以在这里运行它

对于 10000 个元素的数组 - 你可以在这里运行它

关于解决方案 ABCDEGHJLM,如下代码段所示

// https://stackoverflow.com/a/10499519/860099 函数 A(arr1,arr2) { return _.union(arr1,arr2) } // https://stackoverflow.com/a/53149853/860099 函数 B (arr1,arr2) { return _.unionWith(arr1, arr2, _.isEqual); } // https://stackoverflow.com/a/27664971/860099 函数 C(arr1,arr2) { return [...new Set([...arr1,...arr2])] } // https: //stackoverflow.com/a/48130841/860099 function D(arr1,arr2) { return Array.from(new Set(arr1.concat(arr2))) } // https://stackoverflow.com/a/23080662/ 860099 函数 E(arr1,arr2) { return arr1.concat(arr2.filter((item) => arr1.indexOf(item) < 0)) } // https://stackoverflow.com/a/28631880/860099 函数G(arr1,arr2) { var hash = {};变量我; for (i = 0; i < arr1.length; i++) { hash[arr1[i]] = true; } for (i = 0; i < arr2.length; i++) { hash[arr2[i]] = true; } 返回 Object.keys(hash); } // https://stackoverflow.com/a/13847481/860099 函数 H(a, b){ var hash = {}; var ret = []; for(var i=0; i < a.length; i++){ var e = a[i]; if (!hash[e]){ hash[e] = true; ret.push(e); } } for(var i=0; i < b.length; i++){ var e = b[i]; if (!hash[e]){ hash[e] = true; ret.push(e); } } 返回 ret; } // https://stackoverflow.com/a/1584377/860099 函数 J(arr1,arr2) { function arrayUnique(array) { var a = array.concat(); for(var i=0; i g => x => f(g(x));常量应用 = f => a => f(a);常量翻转 = f => b => a => f(a) (b); const concat = xs => y => xs.concat(y);常量 afrom = apply(Array.from); const createSet = xs => new Set(xs);常量过滤器 = f => xs => xs.filter(apply(f)); const dedupe = comp(afrom) (createSet); const union = xs => ys => { const zs = createSet(xs);返回 concat(xs) ( filter(x => zs.has(x) ? false : zs.add(x) ) (ys)); } return union(dedupe(arr1)) (arr2) } // ------------- // 测试 // ------------- var array1 = [ "维金德拉","辛格"]; var array2 = ["Singh", "Shakya"]; [A,B,C,D,E,G,H,J,L,M].forEach(f=> { console.log(`${f.name} [${f([...array1] ,[...array2])}]`); }) 这个片段只展示了在性能测试中使用的函数——它本身并不执行测试!

这是 chrome 的示例测试运行

https://i.stack.imgur.com/khxL8.png

更新

我删除了案例 F、I、K,因为它们修改了输入数组并且基准测试给出了错误的结果


你为什么不改进第一个片段并删除代码重复?
@Marco我不知道如何在不损失性能或简单性的情况下改进第一个片段-但我对您的解决方案持开放态度-随时创建新的答案,您可以通过这种方式改进此解决方案-每个人都会很高兴:)
@KamilKiełczewski:小心!我强烈怀疑测试中存在错误。当您添加带有数组长度的 console.log 时,您会发现在大多数情况下长度为 0。感觉每次运行之间阵列都没有正确重置。然后当然,合并两个空数组是一个非常快的操作;)这似乎得到了这个答案 stackoverflow.com/a/66129415/2137476 的证实,其中 K 解决方案很快,但比 C 解决方案少(小心;只看 % 比较;片段中有错误,并且计时错误)
我证实了我的怀疑。我更新了测试台,以便从未修改的 json 解析数组。显然,每次测试都会慢一点,但不影响排名。并且K测试明显慢于C、D、L& M 测试(在 Mac Chrome 上)。 jsbench.me/mpklq0sj6l/1
@Pitouli你是对的-我更新答案并删除了更改输入数组F,I,K的解决方案-因为基准测试给出了错误的结果(当我将来有更多时间时,我将尝试再次对丢弃的解决方案进行基准测试)
u
user1079877

对于 ES6,只有一行:

a = [1, 2, 3, 4]
b = [4, 5]
[...new Set(a.concat(b))]  // [1, 2, 3, 4, 5]

P
Peter Mortensen

最好的解决方案...

您可以通过点击直接在浏览器控制台中检查...

无重复

a = [1, 2, 3];
b = [3, 2, 1, "prince"];

a.concat(b.filter(function(el) {
    return a.indexOf(el) === -1;
}));

有重复

["prince", "asish", 5].concat(["ravi", 4])

如果您想要不重复,您可以从这里尝试更好的解决方案 - Shouting Code

[1, 2, 3].concat([3, 2, 1, "prince"].filter(function(el) {
    return [1, 2, 3].indexOf(el) === -1;
}));

在 Chrome 浏览器控制台上尝试

 f12 > console

输出:

["prince", "asish", 5, "ravi", 4]

[1, 2, 3, "prince"]

它不会从输出数组中删除重复项。
H
Hero Qu

我的一分钱:

Array.prototype.concat_n_dedupe = function(other_array) {
  return this
    .concat(other_array) // add second
    .reduce(function(uniques, item) { // dedupe all
      if (uniques.indexOf(item) == -1) {
        uniques.push(item);
      }
      return uniques;
    }, []);
};

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

var result = array1.concat_n_dedupe(array2);

console.log(result);

它没有使用 ES6 中的任何新内容,我错过了什么吗?
@Bergi:是的,你是对的。谢谢你的注意。不知何故,我在玩这个脚本,可能有一些带有 ES6 功能的版本,但现在它包含几个世纪以来一直存在的 indexOf。我的错,对不起。
T
TopW3

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

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

P
Peter Mortensen

你可以简单地使用 Underscore.js 的 => uniq 来实现它:

array3 = _.uniq(array1.concat(array2))

console.log(array3)

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


m
meder omuraliev

新解决方案(使用 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:如果订单无关紧要,那么我该怎么做
它不是为 Array.prototype 定义的标准 ECMAScript 方法,尽管我知道您可以轻松地为 IE 和其他不支持它的浏览器定义它。
请注意,此算法为 O(n^2)。
你的答案是什么算法?
@meder:我的算法是联合算法。联合本身在 O(n+m) 中完成,但排序最多需要 O(n·log n+m·log m)。所以整个算法是O(n·log n+m·log m)。
S
Sarfaraaz

可以使用 Set 来完成。

var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"]; var array3 = array1.concat(array2); var tempSet = new Set(array3); array3 = Array.from(tempSet); //显示输出 document.body.querySelector("div").innerHTML = JSON.stringify(array3);

临时文本


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


我在 w3schools 中找不到它,这就是我写它的原因。 w3schools.com/jsref/jsref_obj_array.asp 是否需要 from 参数?
谢谢@Gumbo 和@meder - 现在要更改我的书签。我还没有在 js 中做任何严肃的事情,我使用 w3schools 进行随意参考(这就是我所需要的)——可能这就是我没有意识到这一点的原因。
MDC 说 indexOf 需要 javascript 1.6 假设普通浏览器(>= FF2,> IE6 等)会支持它是否安全?
IE6 不支持 Array.prototype.indexOf,只需粘贴 Mozilla 提供的支持方法,这样 IE 就不会报错。
使用 indexOf 更新。通过删除注释部分来清理代码。 @meder - 再次感谢。
L
Lajos Mészáros
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]

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

这个的好处是性能,一般来说,在使用数组时,您会链接过滤器、映射等方法,因此您可以添加该行,它会将数组 2 与数组 1 连接和重复数据删除,而无需对后面的引用一个(当你链接你没有的方法时),例如:

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,这将是尊重链的唯一方法——定义一个新函数会破坏它——所以我认为这样的事情是实现这一目标的唯一方法)


佚名

ES2015 的函数式方法

遵循函数式方法,两个 Arrayunion 只是 concatfilter 的组合。为了提供最佳性能,我们采用原生 Set 数据类型,该数据类型针对属性查找进行了优化。

无论如何,与 union 函数相关的关键问题是如何处理重复项。以下排列是可能的:

Array A      + Array B

[unique]     + [unique]
[duplicated] + [unique]
[unique]     + [duplicated]
[duplicated] + [duplicated]

前两个排列很容易用一个函数处理。但是,最后两个更复杂,因为只要您依赖 Set 查找,您就无法处理它们。由于切换到普通的旧 Object 属性查找会严重影响性能,因此以下实现只是忽略了第三和第四排列。您必须构建一个单独的 union 版本来支持它们。

// 小的、可重用的辅助函数 const comp = f => g => x => f(g(x));常量应用 = f => a => f(a);常量翻转 = f => b => a => f(a) (b); const concat = xs => y => xs.concat(y);常量 afrom = apply(Array.from); const createSet = xs => new Set(xs);常量过滤器 = f => xs => xs.filter(apply(f)); // 去重 const dedupe = comp(afrom) (createSet); // 实际的联合函数 const union = xs => ys => { const zs = createSet(xs);返回 concat(xs) ( filter(x => zs.has(x) ? false : zs.add(x) ) (ys)); } // 模拟数据 const xs = [1,2,2,3,4,5];常量 ys = [0,1,2,3,3,4,5,6,6]; // 这里我们去 console.log( "unique/unique", union(dedupe(xs)) (ys) ); console.log("重复/唯一", union(xs) (ys) );

从这里开始,实现一个 unionn 函数变得微不足道,它接受任意数量的数组(受 naomik 评论的启发):

// 小的、可重用的辅助函数 const uncurry = f => (a, b) => f(a) (b); const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);常量应用 = f => a => f(a);常量翻转 = f => b => a => f(a) (b); const concat = xs => y => xs.concat(y); const createSet = xs => new Set(xs);常量过滤器 = f => xs => xs.filter(apply(f)); // union 和 unionn const union = xs => ys => { const zs = createSet(xs);返回 concat(xs) ( filter(x => zs.has(x) ? false : zs.add(x) ) (ys)); } const unionn = (head, ...tail) => foldl(union) (head) (tail); // 模拟数据 const xs = [1,2,2,3,4,5];常量 ys = [0,1,2,3,3,4,5,6,6];常量 zs = [0,1,2,3,4,5,6,7,8,9]; // 这里我们去 console.log( unionn(xs, ys, zs) );

原来 unionn 只是 foldl(又名 Array.prototype.reduce),它将 union 作为它的 reducer。注意:由于该实现不使用额外的累加器,因此当您在不带参数的情况下应用它时会引发错误。


一些反馈:我注意到 flipnotf 未使用。此外,unionBy 谓词泄露了实现细节(需要隐含 Set 类型的知识)。如果你能做这样的事情可能会很好:union = unionBy (apply)unionci = unionBy (p => x => p(x.toLowerCase()))。这样,用户只需将分组值发送给 p - 只是一个想法^_^
zs 变量声明也缺少 var/let 关键字
这是澄清 [gist: unionBy.js] 的代码片段
@naomik 在重新考虑我的解决方案一段时间后,我不再确定它是否是传递谓词的正确方法。您所获得的只是对第二个数组的每个元素的转换。我想知道这种方法是否解决的不仅仅是玩具问题。
在这种情况下,功能方法有什么好处?
D
DevWL

DeDuplicate 单个或 Merge and DeDuplicate 多个数组输入。下面的例子。

使用 ES6 - Set, for of, destructuring

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

SHORT FUNCTION DEFINITION(只有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,因为结果被忽略
我用 return Array.from(set.values()); , 因为 vscode 给出返回错误 [...set];
T
Tushar Walzade

var array1 = [“一”,“二”]; var array2 = [“二”,“三”]; var collectionOfTwoArrays = [...array1, ...array2]; var uniqueList = array => [...new Set(array)]; console.log('收藏:'); console.log(collectionOfTwoArrays); console.log('没有重复的集合:'); console.log(uniqueList(collectionOfTwoArrays));