如何在JavaScript中合并两个数组并重复删除项目
我有两个JavaScript数组:
var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"];
我想要的输出是:
var array3 = ["Vijendra","Singh","Shakya"];
输出数组应该重复删除单词。
如何在JavaScript中合并两个数组,以便按照插入原始数组的相同顺序从每个数组中获取唯一项目?
要合并数组(不删除重复项)使用Array.concat
:
var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"]; var array3 = array1.concat(array2); // Merges both arrays // [ 'Vijendra', 'Singh', 'Singh', 'Shakya' ]
由于没有“内置”的方式来删除重复( 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();
这也将保持数组的顺序(即不需要sorting)。
由于很多人对Array.prototype
原型扩展和for 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; } });
用Underscore.js或者Lo-Dash你可以做:
_.union([1, 2, 3], [101, 2, 1, 10], [2, 1]); => [1, 2, 3, 101, 10]
首先连接两个数组,然后滤除唯一的项目。
var a = [1, 2, 3], b = [101, 2, 1, 10]; var c = a.concat(b); var d = c.filter(function (item, pos) {return c.indexOf(item) == pos}); // d is [1,2,3,101,10]
http://jsfiddle.net/simo/98622/
编辑
正如@Dmitry(见下面的第二个评论)所build议的,一个更有效的解决scheme是过滤掉b
的唯一项,然后连接a
var a = [1, 2, 3], b = [101, 2, 1, 10]; var c = a.concat(b.filter(function (item) { return a.indexOf(item) < 0; })); // d is [1,2,3,101,10]
这是一个使用扩展运算符和数组generics的ECMAScript 6解决scheme。
目前它只适用于Firefox,可能还有Internet Explorer Technical Preview。
但是,如果你使用巴别塔 ,你现在就可以拥有它。
// Input: [ [1, 2, 3], [101, 2, 1, 10], [2, 1] ] // Output: [1, 2, 3, 101, 10] function mergeDedupe(arr) { return [...new Set([].concat(...arr))]; }
ES6
array1.push(...array2) // => don't remove duplication
要么
[...array1,...array2] // => don't remove duplication
要么
[...new Set([...array1 ,...array2])]; // => remove duplication
这是一个略有不同的循环。 通过最新版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
filter:〜445k ops / s
lodash:308k ops / s
for循环:225k ops / s
一个评论指出,我的一个设置variables导致我的循环提前rest,因为它不需要初始化一个空的数组来写入。 我同意这一点,所以我已经把testing改写成了比赛场地,甚至包括更快的选项。
http://jsperf.com/merge-two-arrays-keeping-only-unique-values/21
var whileLoopAlt = function(array1, array2) { var array3 = []; var arr = array1.concat(array2); var len = arr.length; var assoc = {}; while(len--) { var itm = arr[len]; if(!assoc[itm]) { // Eliminate the indexOf call array3.unshift(itm); assoc[itm] = true; } } return array3; };
在这个替代的解决scheme中,我已经组合了一个答案的关联数组解决scheme,以消除循环中的.indexOf()
调用,这是第二个循环减慢了很多事情,包括其他用户build议的其他优化他们的答案也是如此。
这里的每个值(i-1)的双循环的最佳答案仍然明显较慢。 lodash依然强大,我仍然会把它推荐给任何不介意在项目中添加一个库的人。 对于那些不想要的人来说,我的while循环仍然是一个很好的答案,filter的答案在这里有一个非常强烈的performance,在写作时用最新的Canary Chrome(44.0.2360)在我的testing中击败了所有人。
如果你想提高速度,请查看Mike的回答和Dan Stocker的回答 。 在经历几乎所有可行的答案之后,这些是所有结果中最快的。
你可以简单地用ECMAScript 6来完成,
var array1 = ["Vijendra", "Singh"]; var array2 = ["Singh", "Shakya"]; var array3 = [...new Set([...array1 ,...array2])]; console.log(array3); // ["Vijendra", "Singh", "Shakya"];
- 使用spread运算符来连接数组。
- 使用Set来创build一组不同的元素。
- 再次使用spread运算符将Set转换为数组。
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; };
一个更好的数组合并函数。
只要投入我的两分钱。
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。 我真的不知道浏览器如何做这个散列,但它在数千个数据点上performance良好。
你为什么不使用一个对象? 它看起来像你试图build模一套。 但是,这不会保留订单。
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 }
只要避开嵌套循环(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); }
我的一个半便士:
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);
//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
简化simo的答案,并把它变成一个很好的function。
function mergeUnique(arr1, arr2){ return arr1.concat(arr2.filter(function (item) { return arr1.indexOf(item) === -1; })); }
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]
最好的解决scheme…
您可以直接在浏览器控制台中点击…
没有重复
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])
如果你想不重复,你可以尝试从这里更好的解决scheme – 喊代码 。
[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"]
你可以简单地使用Underscore.js => uniq来实现它:
array3 = _.uniq(array1.concat(array2)) console.log(array3)
它将打印[“Vijendra”,“Singh”,“Shakya”] 。
新的解决scheme(使用Array.prototype.indexOf
和Array.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; };
在Dojo 1.6+
var unique = []; var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"]; var array3 = array1.concat(array2); // Merged both arrays dojo.forEach(array3, function(item) { if (dojo.indexOf(unique, item) > -1) return; unique.push(item); });
更新
见工作代码。
合并无限数量的数组或非数组并保持唯一:
function flatMerge() { return Array.prototype.reduce.call(arguments, function (result, current) { if (!(current instanceof Array)) { if (result.indexOf(current) === -1) { result.push(current); } } else { current.forEach(function (value) { console.log(value); if (result.indexOf(value) === -1) { result.push(value); } }); } return result; }, []); } flatMerge([1,2,3], 4, 4, [3, 2, 1, 5], [7, 6, 8, 9], 5, [4], 2, [3, 2, 5]); // [1, 2, 3, 4, 5, 7, 6, 8, 9] flatMerge([1,2,3], [3, 2, 1, 5], [7, 6, 8, 9]); // [1, 2, 3, 5, 7, 6, 8, 9] flatMerge(1, 3, 5, 7); // [1, 3, 5, 7]
假设原始数组不需要重复数据删除,这应该是相当快的,保留原始顺序,并且不会修改原始数组…
function arrayMerge(base, addendum){ var out = [].concat(base); for(var i=0,len=addendum.length;i<len;i++){ if(base.indexOf(addendum[i])<0){ out.push(addendum[i]); } } return out; }
用法:
var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"]; var array3 = arrayMerge(array1, array2); console.log(array3); //-> [ 'Vijendra', 'Singh', 'Shakya' ]
最简单的方法是使用concat()
合并数组,然后使用filter()
移除重复项,或者使用concat()
,然后将合并的数组放入Set()
。
第一种方式:
const firstArray = [1,2, 2]; const secondArray = [3,4]; // now lets merge them const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4] //now use filter to remove dups const removeDuplicates = mergedArray.filter((elem, index) => mergedArray.indexOf(elem) === index); // [1,2,3, 4]
第二种方式(但对UI有性能影响):
const firstArray = [1,2, 2]; const secondArray = [3,4]; // now lets merge them const mergedArray = firstArray.concat(secondArray); // [1,2,2,3,4] const removeDuplicates = new Set(mergedArray);
看起来像接受的答案是在我的testing中最慢的;
注意我正在通过Key合并两个对象数组
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width"> <title>JS Bin</title> </head> <body> <button type='button' onclick='doit()'>do it</button> <script> function doit(){ var items = []; var items2 = []; var itemskeys = {}; for(var i = 0; i < 10000; i++){ items.push({K:i, C:"123"}); itemskeys[i] = i; } for(var i = 9000; i < 11000; i++){ items2.push({K:i, C:"123"}); } console.time('merge'); var res = items.slice(0); //method1(); method0(); //method2(); console.log(res.length); console.timeEnd('merge'); function method0(){ for(var i = 0; i < items2.length; i++){ var isok = 1; var k = items2[i].K; if(itemskeys[k] == null){ itemskeys[i] = res.length; res.push(items2[i]); } } } function method1(){ for(var i = 0; i < items2.length; i++){ var isok = 1; var k = items2[i].K; for(var j = 0; j < items.length; j++){ if(items[j].K == k){ isok = 0; break; } } if(isok) res.push(items2[i]); } } function method2(){ res = res.concat(items2); for(var i = 0; i < res.length; ++i) { for(var j = i+1; j < res.length; ++j) { if(res[i].K === res[j].K) res.splice(j--, 1); } } } } </script> </body> </html>
采用ES2015的function性方法
在函数方法之后,两个Array
s的union
只是concat
和filter
的组合。 为了提供最佳的性能,我们使用原生的Set
数据types,这个数据types是针对属性查找而优化的。
无论如何, union
function的关键问题是如何处理重复。 以下排列是可能的:
Array A + Array B [unique] + [unique] [duplicated] + [unique] [unique] + [duplicated] [duplicated] + [duplicated]
前两个排列很容易处理一个单一的function。 但是,最后两个更复杂,因为只要您依赖Set
查找,就无法处理它们。 由于切换到普通的旧Object
属性查找将导致严重的性能下降,下面的实现忽略了第三和第四个排列。 你将不得不build立一个单独的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) );
var MergeArrays=function(arrayOne, arrayTwo, equalityField) { var mergeDictionary = {}; for (var i = 0; i < arrayOne.length; i++) { mergeDictionary[arrayOne[i][equalityField]] = arrayOne[i]; } for (var i = 0; i < arrayTwo.length; i++) { mergeDictionary[arrayTwo[i][equalityField]] = arrayTwo[i]; } return $.map(mergeDictionary, function (value, key) { return value }); }
利用字典和JQuery,你可以合并这两个数组,而不是重复。 在我的例子中,我在对象上使用给定的字段,但可能只是对象本身。
另一种方法来减lessfunc的审查:
function mergeDistinct(arResult, candidate){ if (-1 == arResult.indexOf(candidate)) { arResult.push(candidate); } return arResult; } var array1 = ["Vijendra","Singh"]; var array2 = ["Singh", "Shakya"]; var arMerge = []; arMerge = array1.reduce(mergeDistinct, arMerge); arMerge = array2.reduce(mergeDistinct, arMerge);//["Vijendra","Singh","Shakya"];
如果你想检查独特的对象,那么在你的比较中使用JSON.stringify。
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(JSON.stringify(a[i]) === JSON.stringify(a[j])) a.splice(j--, 1); } } return a; }
Array.prototype.union = function (other_array) { /* you can include a test to check whether other_array really is an array */ other_array.forEach(function(v) { if(this.indexOf(v) === -1) {this.push(v);}}, this); }
One line solution as a segue to LiraNuna's:
let array1 = ["Vijendra","Singh"]; let array2 = ["Singh", "Shakya"]; // Merges both arrays let array3 = array1.concat(array2); //REMOVE DUPLICATE let removeDuplicate = [...new Set(array3)]; console.log(removeDuplicate);
Here is about the most effective one, in terms of computation time. Also it keeps the initial order of elements.
First filter all duplicates from second array, then concatenate what is left to the first one.
var a = [1,2,3]; var b = [5,4,3]; var c = a.concat(b.filter(function(i){ return a.indexOf(i) == -1; })); console.log(c); // [1, 2, 3, 5, 4]
Here is slightly improved (faster) version of it, with a downside, that arrays must not miss values:
var i, c = a.slice(), ci = c.length; for(i = 0; i < b.length; i++){ if(c.indexOf(b[i]) == -1) c[ci++] = b[i]; }