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


1364

我有两个JavaScript数组:

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

我希望输出为:

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

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

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


1
在发布新答案之前,请考虑该问题已经有75多个答案。请确保您的答案提供的信息不属于现有答案。
janniks

[... new Set([... [1、2、3],... [2、3、4]])]结果[1、2、3、4]
Denis Giffeler

如果您想要一个更通用的解决方案,也涵盖深度合并,请看一下这个问题。一些答案也涵盖数组。
Martin Braun

Answers:


1661

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

ES5版本使用Array.concat

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

console.log(array1.concat(array2));

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

281
请注意,此算法为O(n ^ 2)。
Gumbo

7
[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]
-Amarghosh

89
OP接受了使他工作的第一个答案,并签署了它。我们仍在比较彼此的解决方案,查找n-修复问题,提高性能,确保其与任何地方兼容,等等... stackoverflow的
优点

6
我最初对此表示赞成,但是改变了主意。将原型分配给Array.prototype会导致破坏“ for ... in”语句。因此,最好的解决方案可能是使用类似这样的功能,而不是将其分配为原型。有人可能会争辩说,无论如何都不应该使用“ for ... in”语句来迭代数组元素,但是人们经常以这种方式使用它们,因此至少应谨慎使用此解决方案。
代码指挥官

16
你应该总是使用for ... inhasOwnProperty在这种情况下,原型法是好的
mulllhausen

600

使用Underscore.js或Lo-Dash,您可以执行以下操作:

console.log(_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]));
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.15/lodash.min.js"></script>

http://underscorejs.org/#union

http://lodash.com/docs#union


70
或者,甚至比下划线更好的API兼容lodash
布莱恩·亨特

3
@Ygg来自lodash文档。“ 按顺序返回一个唯一值的新数组,该数组存在于一个或多个数组中。”
理查德·阿约特

4
我更喜欢underscore.js。我最终使用的是underscore.flatten(),它比union更好,因为它需要一个数组数组。
weaver

8
@weaver _.flatten合并,但不“重复数据删除”。
GijsjanB

9
快速的性能与lodash相比是最高的答案:jsperf.com/merge-two-arrays-keeping-only-unique-values
slickplaid 2014年

288

首先连接两个数组,然后仅过滤出唯一项:

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 is [1, 2, 3, 101, 10]

编辑

如建议的那样,在性能上更明智的解决方案是在b与级联之前过滤掉其中的唯一项a

var a = [1, 2, 3], b = [101, 2, 1, 10]
var c = a.concat(b.filter((item) => a.indexOf(item) < 0))

console.log(c) // c is [1, 2, 3, 101, 10]


5
原始解决方案的好处是可以消除每个源阵列中的重复项。我想这取决于您要使用的上下文。
theGecko

对于IE6支持,您可以合并其他内容:c = Array.from(new Set(c));
Tobi G.

如果我想实际更改a为add b,那么遍历并使用push会更好吗?a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
2013年

1
提醒那些担心IE6 的人们,当前浏览器的使用情况为caniuse.com/usage-table
pmrotule '16

10
@Andrew:更妙的是:1. var c = [...a, ...b.filter(o => !~a.indexOf(o))]; 2. var c = [...new Set([...a, ...b])];
7vujy0f0hy

203

这是使用扩展运算符和数组泛型的ECMAScript 6解决方案。

目前,它仅适用于Firefox,可能还适用于Internet Explorer技术预览版。

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

const input = [
  [1, 2, 3],
  [101, 2, 1, 10],
  [2, 1]
];
const mergeDedupe = (arr) => {
  return [...new Set([].concat(...arr))];
}

console.log('output', mergeDedupe(input));


14
这应该添加到接受的答案中。该解决方案比当前可行的解决方案效率更高,更优雅,但这是我们不可避免地能够做到的(并且应该在这一领域保持领先)。
EmmaGamma 2015年

这与OP的问题并不完全相同(这似乎比平面图更重要),但要投票赞成,因为它很棒。
jedd.ahyoung

4
很难说这是应该接受的答案,因为问题来自2009年。但是,是的,这不仅更“表现出色”,而且更“优雅”
Cezar Augusto

11
Array.from可以代替传播操作符使用: Array.from(new Set([].concat(...arr)))
Henry Blyth

1
这非常优雅。不幸的是Typescript还不支持。stackoverflow.com/questions/33464504/...
奔鲤鱼

190

ES6

array1.push(...array2) // => don't remove duplication 

要么

[...array1,...array2] //   =>  don't remove duplication 

要么

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

1
第1/2个示例完全没有union+第一个示例会使大Arrays 炸毁堆栈+第三个示例非常慢并且消耗大量内存,因为Array必须构建两个中间的s +第三个示例只能用于union已知的Array编译时的s 数。

那么你会怎么做呢?
DavidNoreña'16

14
Set是去这里的方式
philk

3
请注意,除非具有相同的键值对,否则for set不能对具有相同键值对的两个对象进行重复数据删除。
6

2
不适用于对象数组,因为它将仅合并对象引用,并且不关心对象本身是否相等。
威尔逊·比格斯

87

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

const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));


7
我认为这是使用ES6的“可接受的答案”。
mwieczorek '18

9
@mwieczorek怎么样:const array3 = [...new Set(array1.concat(array2))]
Robby Cornelissen

5
如果您使用的是对象数组,则无法使用
carkod

1
用于合并不同对象而不进行重复的操作:stackoverflow.com/a/54134237/3131433
Rakibul Haq

38

这是一个略有不同的循环。借助最新版本的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的答案Dan Stocker的答案。经过几乎所有可行的答案之后,这些结果是所有结果中最快的。


您的方法存在一个缺陷:将array3的创建置于设置阶段,而该成本仅应是基于While的解决方案得分的一部分。随着这1行的移动,您的解决方案将降至基于for循环的速度。我知道数组可以重用,但是其他算法也可以从不必声明和初始化每个必要的构建块中受益。
doldt 2015年

我同意您的前提@doldt,但不同意您的结果。基于循环的条目删除存在一个基本的设计缺陷,即您必须在删除项目之后重新检查数组的长度,从而导致执行时间变慢。向后循环的while循环不会产生这些影响。这是一个示例,该示例在不改变其原始答案的情况下尽可能多地删除设置变量:jsperf.com/merge-two-arrays-keeping-only-unique-values/19
slickplaid 2015年

@slickplaid链接的测试为空,并且jsperf的下一个修订版本挂在while循环中。
doldt

@doldt我已经在回答中解决了您的疑虑,并为其添加了适当的更新测试。让我知道您是否同意这些结果。另外,我使用关联数组添加了另一个更好的结果。
slickplaid2015年

1
@slickplaid感谢您设置扩展的性能页面。除非我缺少任何东西,否则“ whileLoopAlt2”功能不起作用?它创建一个包含第一个数组和第二个数组的新数组(顺序相反)。为避免混淆,我进行了另一个修订,删除了损坏的功能。我还添加了另一个示例:jsperf.com/merge-two-arrays-keeping-only-unique-values/22
Stephen S

37

您只需使用ECMAScript 6即可做到

var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
  • 使用散布运算符来连接数组。
  • 使用Set创建一组独特的元素。
  • 再次使用散布运算符将Set转换为数组。

2
我收到错误:类型'Set <string>'不是数组类型。
gattsbr

3
如果你因为某些原因不想使用扩操作,还有还有:Array.from(new Set(array1.concat(array2)))
kba

@gattsbr,使用TypeScript时tsconfig.json,您可以添加"downlevelIteration": truecompilerOptions
VincentPerrin.com

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

更好的数组合并功能。


4
var test = ['a', 'b', 'c']; console.log(test); 将打印 ["a", "b", "c", merge: function]
Doubidou

优秀的解决方案。我已经更新了@slickplaid(jsperf.com/merge-two-arrays-keeping-only-unique-values/3)在上面发布的jsperf测试,它似乎是其中最快的一个。
眼镜蛇

@Cobra在Chrome 40.0.2214(最新版本为15年2月18日)上运行时,冒着听起来有些琐碎的危险,这个答案比我的慢了53%。OTOH IE11似乎根本没有针对我的答案进行优化。:) Chrome移动仍然在摇摆。老实说,如果您使用的是我们大多数人都应该使用的lodash / _,则此列表中的真实答案已经很高。:)
slickplaid

@slickplaid是的,即使与lodash / _相比,它也要快很多。我可能最终会在某一点或另一点将我的实现切换到类似于您的实现。:D
眼镜蛇

1
不确定indexOf()方法的成本是多少,但这可能是最快的ES5兼容方法。另外,不需要可变长度的参数也毫无价值。此方法是可链接的。@slickplaid加载库永远不会回答“如何在javascript中做到”这一问题。当然,许多库都具有完成这7行工作的功能。
dehart

19

只需投入我的两分钱。

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

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


做得非常好。通过利用关联数组并避免indexOf和其他操作的循环,可以击败此页面上其他(如果不是全部)其他结果。jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid 2015年

您的“哈希”是String()javascript中的函数。这可能适用于原始值(尽管类型之间存在冲突),但不适用于对象数组。
Bergi '16

我使用类似的解决方案,我允许传递hashCode函数或传递字符串以标识对象中用作哈希键的属性。
罗伯特·贝克

19

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

function merge(a, b) {
    var hash = {}, i;
    for (i=0; i<a.length; i++) {
        hash[a[i]]=true;
    } 
    for (i=0; i<b.length; i++) {
        hash[b[i]]=true;
    } 
    return Object.keys(hash);
}

2
这真是太神奇了,尤其是当您在做弦乐时。数字将需要额外的步骤来保持其原样。如果您不介意(或关心)完成后所有内容都是字符串,则此功能会大大超过所有其他选项。不错的工作。此处的性能结果:jsperf.com/merge-two-arrays-keeping-only-unique-values/21
slickplaid 2015年

18

简化了最佳答案,并将其转变为一个不错的功能:

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

2
我相信这比公认的答案要干净得多。而且看起来ECMAScript 5.1 +支持过滤器,现在已经很支持了。
汤姆·佛伯

这应该已经被接受
Wallop

这要简洁得多。
Mox

15

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

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

2
我为什么要这样 该条件的目的是忽略对象原型中可能存在的属性。
Nick Retallack

12

最好的解决方案...

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

无重复

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

如果您希望没有重复的内容,可以从此处尝试更好的解决方案- 喊代码

[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"]

它不会从输出数组中删除重复项。
Shahar

9

我的一个半便士:

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 '16

@Bergi:是的,你是对的。感谢您的注意。我以某种方式正在使用此脚本,可能有一些带有ES6函数的版本,但是现在它包含indexOf,已有数百年历史了。我的错,对不起。
英雄曲


7

对于ES6,只需一行:

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

7

我知道这个问题与对象数组无关,但是搜索者的确到了这里。

因此值得将来的读者添加正确的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} ]

6
//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中找不到它,所以才写了它。w3schools.com/jsref/jsref_obj_array.asp是否接受from参数btw?
2009年

感谢@Gumbo和@meder-现在要更改我的书签。我还没有在JS中做任何严肃的事情,我使用w3schools作为临时参考(这是我所需要的全部)-也许这就是为什么我没有意识到这一点。
Amarghosh,2009年

MDC说indexOf需要javascript 1.6可以安全地假设通用浏览器(> = FF2,> IE6等)会支持它吗?
2009年

4
IE6不支持Array.prototype.indexOf,只需粘贴Mozilla提供的支持方法,以使IE不会引发错误。
meder omuraliev 09年

使用更新indexOf。通过删除注释部分来清理代码。@meder-再次感谢。
Amarghosh,2009年

6

新解决方案(使用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
这不是为Array.prototype定义的标准ECMAScript方法,尽管我知道您可以轻松地为IE和其他不支持它的浏览器定义它。
meder omuraliev 09年

请注意,此算法为O(n ^ 2)。
Gumbo

您的答案是什么算法?
meder omuraliev 09年

@meder:我的算法是联合算法。联合本身以O(n + m)完成,但排序最多需要O(n·log n + m·log m)。因此整个算法为O(n·log n + m·log m)。
Gumbo

6

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


6

合并两个数组有很多解决方案。它们可以分为两个主要类别(使用诸如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)));

5
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
array1.concat(array2).filter((value, pos, arr)=>arr.indexOf(value)===pos)

关于这一点的好处是性能,通常来说,当您使用数组时,您可以使用过滤器,映射等链接方法,因此您可以添加该行,它将与array1进行array2的重复数据删除和重复数据删除,而无需引用后面的内容。一种(当您没有链接方法时),例如:

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,这是尊重链的唯一方法-定义一个新函数会破坏它-因此我认为类似的事情是实现此目的的唯一方法)


4

您可以尝试以下方法:

const union = (a, b) => Array.from(new Set([...a, ...b]));

console.log(union(["neymar","messi"], ["ronaldo","neymar"]));


3

ES2015的功能性方法

以下功能法一unionArrays是刚刚组成concatfilter。为了提供最佳性能,我们求助于本机Set数据类型,该属性针对属性查找进行了优化。

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

Array A      + Array B

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

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


// small, reusable auxiliary functions

const comp = f => g => x => f(g(x));
const apply = f => a => f(a);
const flip = f => b => a => f(a) (b);
const concat = xs => y => xs.concat(y);
const afrom = apply(Array.from);
const createSet = xs => new Set(xs);
const filter = f => xs => xs.filter(apply(f));


// de-duplication

const dedupe = comp(afrom) (createSet);


// the actual union function

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


// mock data

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


// here we go

console.log( "unique/unique", union(dedupe(xs)) (ys) );
console.log( "duplicated/unique", union(xs) (ys) );

从这里开始,实现一个unionn函数很简单,该函数可以接受任意数量的数组(受naomik的评论启发):

// small, reusable auxiliary functions

const uncurry = f => (a, b) => f(a) (b);
const foldl = f => acc => xs => xs.reduce(uncurry(f), acc);

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


// union and unionn

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

const unionn = (head, ...tail) => foldl(union) (head) (tail);


// mock data

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


// here we go

console.log( unionn(xs, ys, zs) );

事实证明unionn只是foldl(aka Array.prototype.reduce),它union作为它的减速器。注意:由于实现不使用其他累加器,因此在不带参数的情况下应用时将抛出错误。


1
几个反馈:我注意到了,flip并且notf没有使用。还要确定unionBy泄漏实现的细节(需要隐式Set类型知识)。如果您可以执行以下操作可能会很好:union = unionBy (apply)unionci = unionBy (p => x => p(x.toLowerCase()))。这样,用户就可以将分组值发送给任何对象p–只是一个想法^ _ ^
谢谢您

zs变量声明也缺乏var/ let关键字
谢谢您

1
这是一个澄清的代码段[要点:unionBy.js ]
谢谢您,

@naomik重新思考了一段时间后,我不再确定这是否是传递谓词的正确方法。您获得的只是第二个数组中每个元素的转换。我想知道这种方法是否可以解决玩具问题以外的其他问题。

3

为此,这里是单行解决方案:

const x = [...new Set([['C', 'B'],['B', 'A']].reduce( (a, e) => a.concat(e), []))].sort()
// ['A', 'B', 'C']

可读性不强,但可能会帮助某人:

  1. 应用将初始累加器值设置为空数组的reduce函数。
  2. reduce函数使用concat将每个子数组附加到累加器数组上。
  3. 结果作为构造函数参数传递,以创建一个新的 Set
  4. Spread运算符用于将转换Set为数组。
  5. sort()函数将应用于新数组。

2
另外,reduce()您也可以使用Array.from(set)
Eran Goldin

3

重复数据删除单个或合并和重复数据删除多个数组输入。下面的例子。

使用ES6-进行销毁

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

简短功能定义(仅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]的
makkasi

3
var arr1 = [1, 3, 5, 6];
var arr2 = [3, 6, 10, 11, 12];
arr1.concat(arr2.filter(ele => !arr1.includes(ele)));
console.log(arr1);

output :- [1, 3, 5, 6, 10, 11, 12]

3

var array1 = ["one","two"];
var array2 = ["two", "three"];
var collectionOfTwoArrays = [...array1, ...array2];    
var uniqueList = array => [...new Set(array)];
console.log('Collection :');
console.log(collectionOfTwoArrays);    
console.log('Collection without duplicates :');
console.log(uniqueList(collectionOfTwoArrays));

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.