如何在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个回答

2

最简单的方法是使用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);

真的很想用第二种方法,但在 UI 更新循环中创建一个新的 Set 可能会很昂贵。 - newguy
我之前不知道这个问题,感谢你指出来。我会更新我的回答。顺便问一下,你能提供相关链接吗? - Stelios Voskos
嗯,我不认为有链接。我只是根据我在 HTML5 画布渲染循环中使用数组的经验说的。 - newguy

2

减少它们!!!

这种替代方案不是显式地合并和去重,而是使用另一个数组来缩小一个数组,使第一个数组的每个值都可以被累积迭代和解构,通过利用递归的数组持久性来忽略已经包含的值。

array2.reduce(reducer, array1.reduce(reducer, []))

测试示例:

var array1 = ["Vijendra","Singh","Singh"];
var array2 = ["Singh", "Shakya", "Shakya"];
const reducer = (accumulator, currentValue) => accumulator.includes(currentValue) ? accumulator : [...accumulator, currentValue];

console.log(
  array2.reduce(reducer, array1.reduce(reducer, []))
);

// a reduce on first array is needed to ensure a deduplicated array used as initial value on the second array being reduced

结论

当需要避免枯燥的for-each方法时,这种方法更加优雅和有用(并不是说它没有用处)。

处理concat()在去重方面的限制。

无需使用Underscore.js、JQuery或Lo-Dash等外部库,也不需要麻烦地创建任何内置函数来实现所需的合并和去重效果。

哦,还有!它可以作为一行代码完成!


这个答案得益于ES5(ECMAScript 2015)美丽的include()和华丽的reduce()。


1
这是一个简单的例子:

var unique = function(array) {
    var unique = []
    for (var i = 0; i < array.length; i += 1) {
        if (unique.indexOf(array[i]) == -1) {
            unique.push(array[i])
        }
    }
    return unique
}

var uniqueList = unique(["AAPL", "MSFT"].concat(["MSFT", "BBEP", "GE"]));

我们定义unique(array)函数来删除冗余元素,并使用concat函数将两个数组合并。

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

1
一行代码解决方案,作为LiraNuna的引子:
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);

1
为了提供更简单、更优雅的解决方案,在当今时代,可以使用现有的库:
import {pipe, concat, distinct} from 'iter-ops';

// our inputs:
const array1 = ['Vijendra', 'Singh'];
const array2 = ['Singh', 'Shakya'];

const i = pipe(
    array1,
    concat(array2), // adding array
    distinct() // making it unique
);

console.log([...i]); //=> ['Vijendra', 'Singh', 'Shakya']

这是一段高性能代码,因为我们只迭代了一次,而且代码非常易读。

P.S. 我是iter-ops的作者。


1
我正在尝试这个,谢谢你的库兄弟。 - Dean Van Greunen

1

另一种使用reduce函数的方法供您审查:

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

1

制作了一个测试器,用于检查一些性能导向答案的速度。欢迎添加更多内容。到目前为止,Set 是最简单和最快的选项(随着记录数量增加,差距越大),至少对于简单的 Number 类型。

const records = 10000, //max records per array
  max_int = 100, //max integer value per array
  dup_rate = .5; //rate of duplication
let perf = {}, //performance logger,
  ts = 0,
  te = 0,
  array1 = [], //init arrays
  array2 = [],
  array1b = [],
  array2b = [],
  a = [];

//populate randomized arrays
for (let i = 0; i < records; i++) {
  let r = Math.random(),
    n = r * max_int;
  if (Math.random() < .5) {
    array1.push(n);
    r < dup_rate && array2.push(n);
  } else {
    array2.push(n);
    r < dup_rate && array1.push(n);
  }
}
//simple deep copies short of rfdc, in case someone wants to test with more complex data types
array1b = JSON.parse(JSON.stringify(array1));
array2b = JSON.parse(JSON.stringify(array2));
console.log('Records in Array 1:', array1.length, array1b.length);
console.log('Records in Array 2:', array2.length, array2b.length);

//test method 1 (jsperf per @Pitouli)
ts = performance.now();
for (let i = 0; i < array2.length; i++)
  if (array1.indexOf(array2[i]) === -1)
    array1.push(array2[i]); //modifies array1
te = performance.now();
perf.m1 = te - ts;
console.log('Method 1 merged', array1.length, 'records in:', perf.m1);
array1 = JSON.parse(JSON.stringify(array1b)); //reset array1

//test method 2 (classic forEach)
ts = performance.now();
array2.forEach(v => array1.includes(v) ? null : array1.push(v)); //modifies array1
te = performance.now();
perf.m2 = te - ts;
console.log('Method 2 merged', array1.length, 'records in:', perf.m2);

//test method 3 (Simplest native option)
ts = performance.now();
a = [...new Set([...array1, ...array2])]; //does not modify source arrays
te = performance.now();
perf.m3 = te - ts;
console.log('Method 3 merged', a.length, 'records in:', perf.m3);

//test method 4 (Selected Answer)
ts = performance.now();
a = array1.concat(array2); //does not modify source arrays
for (let i = 0; i < a.length; ++i) {
  for (let j = i + 1; j < a.length; ++j) {
    if (a[i] === a[j])
      a.splice(j--, 1);
  }
}
te = performance.now();
perf.m4 = te - ts;
console.log('Method 4 merged', a.length, 'records in:', perf.m4);

//test method 5 (@Kamil Kielczewski)
ts = performance.now();

function K(arr1, arr2) {
  let r = [],
    h = {};

  while (arr1.length) {
    let e = arr1.shift(); //modifies array1
    if (!h[e]) h[e] = 1 && r.push(e);
  }

  while (arr2.length) {
    let e = arr2.shift(); //modifies array2
    if (!h[e]) h[e] = 1 && r.push(e);
  }

  return r;
}
a = K(array1, array2);
te = performance.now();
perf.m5 = te - ts;
console.log('Method 5 merged', a.length, 'records in:', perf.m4);
array1 = JSON.parse(JSON.stringify(array1b)); //reset array1
array2 = JSON.parse(JSON.stringify(array2b)); //reset array2


for (let i = 1; i < 6; i++) {
  console.log('Method:', i, 'speed is', (perf['m' + i] / perf.m1 * 100).toFixed(2), '% of Method 1');
}


似乎对于项目数量有很高的敏感度。在我的机器上(Mac with Chrome)少于400个项目时,Method 1是最快的。对于更多的项目,Method 3是最快的。但由于对于小数字性能并不重要,并且考虑到Method 3远远是最简单易读的方法,因此它应该成为推荐的方法。 - Pitouli
1
我通过将数字替换为文本字符串进行了测试。结果仍然相同:对于小数据集(使用字符串的项少于50个),方法1更快,而当大小增加时,Set速度非常快。https://gist.github.com/Pitouli/3165d59ae3a1fb90bf35ee7dbbebc28f - Pitouli

1
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,您可以合并这两个数组并且不会得到重复项。在我的示例中,我正在使用对象上的给定字段,但也可以只使用对象本身。

1
这是我的第二个答案,但我相信它是最快的?我想让有人为我检查并在评论中回复。
我的第一次尝试大约达到了99k ops/sec,而这一次则显示为390k ops/sec,而其他jsperf测试的领先值为140k(对于我来说)。

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

我尽可能地减少了数组交互,这次看起来我获得了一些性能优化。
function findMerge(a1, a2) {
    var len1 = a1.length;

    for (var x = 0; x < a2.length; x++) {
        var found = false;

        for (var y = 0; y < len1; y++) {
            if (a2[x] === a1[y]) {
                found = true;
                break;
            }
        }

        if(!found){
            a1.push(a2.splice(x--, 1)[0]);
        }
    }

    return a1;
}

编辑:我对我的函数进行了一些更改,与jsperf网站上的其他函数相比,性能有了显著的改善。


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