什么是N个数组连接的最有效方法?

380

如何在JavaScript中高效地连接N个对象数组?

这些数组是可变的,结果可以存储在其中一个输入数组中。


4
可能是JavaScript中合并/平铺数组的数组的重复问题。 - rofrol
24个回答

443

如果您要连接两个以上的数组,concat()是为了便利和性能而使用的方法。

var a = [1, 2], b = ["x", "y"], c = [true, false];
var d = a.concat(b, c);
console.log(d); // [1, 2, "x", "y", true, false];

要将两个数组连接起来,可以利用push方法接受多个参数的特性,将一个数组的元素添加到另一个数组的末尾,而无需创建新数组。使用slice()方法也可以代替concat()方法,但是似乎没有性能上的优势

var a = [1, 2], b = ["x", "y"];
a.push.apply(a, b);
console.log(a); // [1, 2, "x", "y"];

在ECMAScript 2015及更高版本中,这甚至可以进一步减少为

a.push(...b)

然而,对于大型数组(约为100,000个成员或更多),将元素数组传递给push的技术(使用apply()或ECMAScript 2015扩展运算符)可能会失败。 对于这样的数组,使用循环是更好的方法。有关详细信息,请参见 https://dev59.com/VnM_5IYBdhLWcg3wcSx_#17368101


1
我认为你的测试可能有误:a.concat(b) 测试用例似乎不必要地复制了数组 a,然后将其丢弃。 - ninjagecko
1
@ninjagecko:你说得对。我已经更新了它:http://jsperf.com/concatperftest/6。对于创建一个新数组,将两个现有数组连接起来的特定情况,似乎`concat()`通常更快。对于在原地将数组连接到现有数组的情况,`push()`是正确的方法。我已经更新了我的答案。 - Tim Down
22
我可以证实,使用push.apply方法将单个数组扩展多个新数组,与简单的concat调用相比,具有巨大的性能优势(约为100倍)。我正在处理v8/node.js中非常长的短整数列表。 - chbrown
1
一种更加简洁的方式是 a.push(...b); - dan
1
@dinvlad:没错,但只适用于ES6环境。我已在我的回答中添加了一条注释。 - Tim Down
显示剩余8条评论

198
[].concat.apply([], [array1, array2, ...])

效率的证明: http://jsperf.com/multi-array-concat/7

评论中提到Tim Supinie可能会导致解释器超出调用堆栈大小。这可能取决于JavaScript引擎,但我至少在Chrome上也遇到了“Maximum call stack size exceeded”的情况。测试案例:[].concat.apply([], Array(300000).fill().map(_=>[1,2,3]))。(我也使用了当前被接受的答案得到了相同的错误,因此,不管选择哪种解决方案,如果你希望预料到这种用例或为其他人构建库,都需要进行特殊测试。)


3
在Chrome上,这个方法的效率似乎与重复使用.push(#,#,...,#)的答案差不多。Tim Down所选择的答案可能也存在一个错误。该链接是一个性能比较,比较的是将多个数组连接起来的方法(不仅仅是2个),测试了多种可能长度。 - ninjagecko
1
这个答案在不预先知道N的情况下特别有用,例如当您有一个长度任意的数组数组并希望将它们全部连接起来时。 - jfriend00
5
使用ES6和展开运算符,它变得更加简单: [].concat(...[array1, array2, ...])嗯,第二个三个点有点不幸。第一个三个点是展开运算符。参考链接:https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Operators/Spread_operator - Eydrian
@GilEpshtain:我从来没有这么说过。你误解了我的解释。多参数函数和非多参数函数不同。如果人们愿意,可以自由编写它。再一次地说,你的方法没有问题,对于少量参数是好的,但对于更大数量的参数不好。它基本上是相同的方法。 - ninjagecko
1
仅供参考,如果N非常大,则此方法可能会填满调用栈。我尝试了一个N约为225K的案例,并遇到了这个问题。 - Tim Supinie
显示剩余10条评论

61

新回答

要使用ES6处理多个数组的数组,请使用以下代码:

arr.flat();
例如:
const arr = [[1, 2, 3], [4, 5, 6], [7, 8 ,9]];
const newArr = arr.flat();
// output: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

这将适用于Node版本大于11和现代浏览器。


旧的答案

(如果需要适用于旧版本的Node,可以保留此处内容):

对于多个数组的数组和ES6,请使用

Array.prototype.concat(...arr);

例如:

const arr = [[1, 2, 3], [4, 5, 6], [7, 8 ,9]];
const newArr = Array.prototype.concat(...arr);
// output: [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]

1
此外,您可以使用 newArr = Array.from(new Set(newArr)); 来删除重复元素。 - Darius
为什么在 TypeScript 中,这会变成 any[]?类型已经被指定了 - 很奇怪。 - Simon_Weaver
这在处理大型数组时效率非常低下。https://jsperf.com/flatten-array-203948/[].concat.apply([], ...arr) 在处理大量数据时表现更加优异。 - colemars

50

对于使用ES2015(ES6)的人

您现在可以使用Spread语法来连接数组:

const arr1 = [0, 1, 2],
      arr2 = [3, 4, 5];

const result1 = [...arr1, ...arr2]; // -> [0, 1, 2, 3, 4, 5]

// or...

const result2 = [...arr2, ...arr1]; // -> [3, 4, 5, 0, 1, 2]

5
这个效率如何?根据我的测试结果,包含对象的数组速度非常慢,参见:https://jsperf.com/array-concat-vs-array-push-vs-array-spread/1 - hitautodestruct
1
不幸的是,这个链接不再可用:/ - Don Diego

38

使用Array.prototype.concat.apply来处理多个数组的连接:

var resultArray = Array.prototype.concat.apply([], arrayOfArraysToConcat);

例子:

var a1 = [1, 2, 3],
    a2 = [4, 5],
    a3 = [6, 7, 8, 9];
Array.prototype.concat.apply([], [a1, a2, a3]); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

2
我喜欢这个!可以轻松地使用可变数量的数组进行连接。+1 - Joel

32

concat()方法用于连接两个或多个数组。它不会改变现有的数组,只会返回连接后的副本。

array1 = array1.concat(array2, array3, array4, ..., arrayN);

1
有时候我讨厌 JavaScript,因为它不能修改原始数组。 - Vincent Hoch-Drei
@VincentHoch-Drei concat 专门用于创建新数组,而不会改变原始数组。如果您想要更新 array1,您需要使用 array1.push(...array2, ...array3, ...array4) - adiga

18

如果您正在通过map/filter/sort等处理结果的过程中,并且想要连接数组,请使用reduce

let sorted_nums = ['1,3', '4,2']
  .map(item => item.split(','))   // [['1', '3'], ['4', '2']]
  .reduce((a, b) => a.concat(b))  // ['1', '3', '4', '2']
  .sort()                         // ['1', '2', '3', '4']

我最喜欢这种方法,但请注意,这种方法比“[].concat.apply”慢一百倍。 - Arnie97

10

现在我们可以使用ES6Spread语法来组合多个数组。 不要使用concat()来连接数组,而是尝试使用Spread语法将多个数组组合成一个扁平化的数组。 例如:

var a = [1,2];
var b = [3,4];
var c = [5,6,7];
var d = [...a, ...b, ...c];
// resulting array will be like d = [1,2,3,4,5,6,7]

10

就这样解决了。

let arr = [[1, 2], [3, 4], [5, 6]];
 console.log([].concat(...arr));

9

使用 ES6 进行缩写。

new Set([].concat(...Array));

这个操作会将多个数组进行合并(concat)去重(unique)处理;

let Array = [
  ['vue','babel','npm','gulp','mysql','less','laravel'],
  ['jquery','react','js','css','wordpress','html','bootstrap'],
  ['vue','babel','npm','gulp','mysql','less','laravel'],
  ['angular','cms','js','css','graphql','nodejs','php'],
  ['severless','headless','js','css','design','photoshop','php'],
]

const Boom = new Set([].concat(...Array));


// This is not necessary 
let dStr = '';
Boom.forEach(e=>{
  dStr += e + ' ';
})
document.write(dStr);
<div class="result"></div>


它确实会去除重复项,但如果我不想删除重复的数组怎么办? - Krunal Shah
1
new Set() does remove duplicated items. Just remove it. [].concat(...Array) - ronaldtgi
我想使用 new Set([].concat(...Array)); 但同时我也想在单个表达式中删除重复项。这是否可能? - Krunal Shah
1
完美运行。您还可以使用 [...new Set([].concat(...myArray))] 返回一个 array 而不是一个 Set - Diego Fortes

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