我有两个JavaScript数组:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
我希望输出为:
var array3 = ["Vijendra","Singh","Shakya"];
输出数组应删除重复的单词。
如何在JavaScript中合并两个数组,以使每个数组中的唯一项按插入原始数组中的相同顺序获得?
我有两个JavaScript数组:
var array1 = ["Vijendra","Singh"];
var array2 = ["Singh", "Shakya"];
我希望输出为:
var array3 = ["Vijendra","Singh","Shakya"];
输出数组应删除重复的单词。
如何在JavaScript中合并两个数组,以使每个数组中的唯一项按插入原始数组中的相同顺序获得?
Answers:
仅合并数组(不删除重复项)
Array.concat
:var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
console.log(array1.concat(array2));
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.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;
}
});
[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]
for ... in
与hasOwnProperty
在这种情况下,原型法是好的
使用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>
underscore.flatten()
,它比union更好,因为它需要一个数组数组。
首先连接两个数组,然后仅过滤出唯一项:
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]
a
为add b
,那么遍历并使用push会更好吗?a.forEach(function(item){ if(a.indexOf(item)<0) a.push(item); });
var c = [...a, ...b.filter(o => !~a.indexOf(o))];
2. var c = [...new Set([...a, ...b])];
☺
这是使用扩展运算符和数组泛型的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));
Array.from
可以代替传播操作符使用: Array.from(new Set([].concat(...arr)))
array1.push(...array2) // => don't remove duplication
[...array1,...array2] // => don't remove duplication
[...new Set([...array1 ,...array2])]; // => remove duplication
union
+第一个示例会使大Array
s 炸毁堆栈+第三个示例非常慢并且消耗大量内存,因为Array
必须构建两个中间的s +第三个示例只能用于union
已知的Array
编译时的s 数。
Set
是去这里的方式
使用Set(ECMAScript 2015),就这么简单:
const array1 = ["Vijendra", "Singh"];
const array2 = ["Singh", "Shakya"];
console.log(Array.from(new Set(array1.concat(array2))));
const array3 = [...new Set(array1.concat(array2))]
这是一个略有不同的循环。借助最新版本的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的答案。经过几乎所有可行的答案之后,这些结果是所有结果中最快的。
您只需使用ECMAScript 6即可做到
var array1 = ["Vijendra", "Singh"];
var array2 = ["Singh", "Shakya"];
var array3 = [...new Set([...array1 ,...array2])];
console.log(array3); // ["Vijendra", "Singh", "Shakya"];
Array.from(new Set(array1.concat(array2)))
。
tsconfig.json
,您可以添加"downlevelIteration": true
到compilerOptions
。
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]
只需投入我的两分钱。
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。老实说,我不知道浏览器如何进行哈希处理,但是它在成千上万个数据点上表现良好。
String()
javascript中的函数。这可能适用于原始值(尽管类型之间存在冲突),但不适用于对象数组。
只是避免嵌套循环(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);
}
为什么不使用对象?您似乎正在尝试对集合建模。但是,这不会保留订单。
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))
吗
最好的解决方案...
您可以通过点击...直接在浏览器控制台中进行检查。
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"]
我的一个半便士:
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);
您只需使用Underscore.js的=> uniq即可实现:
array3 = _.uniq(array1.concat(array2))
console.log(array3)
它将打印[“ Vijendra”,“ Singh”,“ Shakya”]。
//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
from
参数btw?
indexOf
。通过删除注释部分来清理代码。@meder-再次感谢。
新解决方案(使用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;
};
可以使用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>
合并两个数组有很多解决方案。它们可以分为两个主要类别(使用诸如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)));
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]
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,这是尊重链的唯一方法-定义一个新函数会破坏它-因此我认为类似的事情是实现此目的的唯一方法)
您可以尝试以下方法:
const union = (a, b) => Array.from(new Set([...a, ...b]));
console.log(union(["neymar","messi"], ["ronaldo","neymar"]));
以下功能法一union
两Array
s是刚刚组成concat
和filter
。为了提供最佳性能,我们求助于本机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
作为它的减速器。注意:由于实现不使用其他累加器,因此在不带参数的情况下应用时将抛出错误。
flip
并且notf
没有使用。还要确定unionBy
泄漏实现的细节(需要隐式Set
类型知识)。如果您可以执行以下操作可能会很好:union = unionBy (apply)
和unionci = unionBy (p => x => p(x.toLowerCase()))
。这样,用户就可以将分组值发送给任何对象p
–只是一个想法^ _ ^
zs
变量声明也缺乏var
/ let
关键字
为此,这里是单行解决方案:
const x = [...new Set([['C', 'B'],['B', 'A']].reduce( (a, e) => a.concat(e), []))].sort()
// ['A', 'B', 'C']
可读性不强,但可能会帮助某人:
Set
。Set
为数组。sort()
函数将应用于新数组。reduce()
您也可以使用Array.from(set)
我写了这个简单的函数,它带有多个数组参数。与上面的解决方案几乎一样,只是具有更实际的用例。此函数不会将重复值仅连接到一个数组中,以便可以在以后的某个阶段将其删除。
简短功能定义(仅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
,结果将被忽略
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));