如何使用另一个数组扩展现有的 JavaScript 数组,而无需创建新数组

IT技术 javascript arrays concatenation
2021-02-06 23:51:37

似乎没有办法用另一个数组扩展现有的 JavaScript 数组,即模拟 Python 的extend方法。

我想实现以下目标:

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

我知道有一种a.concat(b)方法,但它创建了一个新数组,而不是简单地扩展第一个数组。我想要一种算法,当a它明显大于b(即不复制a时,它可以有效地工作

注意:不是How to append something to an array?的副本-- 这里的目标是将一个数组的全部内容添加到另一个数组中,并“就地”完成,即不复制扩展数组的所有元素。

6个回答

.push方法可以采用多个参数。您可以使用扩展运算符将第二个数组的所有元素作为参数传递给.push

>>> a.push(...b)

如果您的浏览器不支持 ECMAScript 6,您可以使用.apply

>>> a.push.apply(a, b)

或者,如果您认为它更清楚:

>>> Array.prototype.push.apply(a,b)

请注意,如果数组b太长,所有这些解决方案都会失败并出现堆栈溢出错误(问题从大约 100,000 个元素开始,具体取决于浏览器)。
如果您不能保证b足够短,则应使用其他答案中描述的基于循环的标准技术。

注意:在这种情况下,第一个返回值不是 [1,2,3,4,5] 而是 5 而 a == [1,2,3,4,5] 之后。
2021-03-14 23:51:37
我认为这是你最好的选择。其他任何事情都将涉及迭代或 apply() 的其他应用
2021-03-27 23:51:37
另一个稍微不那么令人困惑(也不会太长)的调用是:[].push.apply(a, b).
2021-03-28 23:51:37
@Deqing:数组的push方法可以接受任意数量的参数,然后将这些参数推送到数组的后面。所以a.push('x', 'y', 'z')是一个有效的调用,将延长a3元。apply是任何函数的方法,它接受一个数组并使用它的元素,就好像它们都被明确地作为函数的位置元素给出。所以a.push.apply(a, ['x', 'y', 'z'])也会将数组扩展 3 个元素。此外,apply将上下文作为第一个参数(我们a再次传递到那里以附加到a)。
2021-04-01 23:51:37
如果“b”(要扩展的数组)很大(根据我的测试,Chrome 中大约有 > 150000 个条目),则此答案将失败。您应该使用 for 循环,或者更好地使用“b”上的内置“forEach”函数。看我的回答:stackoverflow.com/questions/1374126/...
2021-04-09 23:51:37

2018 年更新更好的答案是我的新答案a.push(...b)不要再给这个投票了,因为它从来没有真正回答过这个问题,但它是 2015 年围绕 Google 首次点击的一次黑客攻击:)


对于那些简单地搜索“JavaScript array extend”并到达这里的人,您可以很好地使用Array.concat.

var a = [1, 2, 3];
a = a.concat([5, 4, 3]);

Concat 将返回新数组的副本,因为线程启动器不想要。但是您可能不在乎(当然对于大多数用途,这会很好)。


还有一些不错的 ECMAScript 6 糖以扩展运算符的形式用于此目的:

const a = [1, 2, 3];
const b = [...a, 5, 4, 3];

(它也复制。)

我听到了你的声音,但我搜索了“javascript append array without created a new array”,然后很遗憾看到 60 次高票的答案没有回答实际问题;)我没有投反对票,因为你在回答中说清楚了。
2021-03-14 23:51:37
问题明确指出:“不创建新数组?”
2021-03-29 23:51:37
@Wilt:这是真的,但答案说明了原因:) 这在 Google 上首次出现了很长时间。其他的解决方案也很丑陋,想要一些符合思想和好的东西。虽然现在arr.push(...arr2)更新更好,并且是对这个特定问题的技术上正确的(tm)答案。
2021-03-30 23:51:37
@Wilt 和我搜索了“js用数组扩展数组”并到了这里,谢谢:)
2021-04-01 23:51:37

您应该使用基于循环的技术。此页面上基于 using 的其他答案.apply对于大型阵列可能会失败。

一个相当简洁的基于循环的实现是:

Array.prototype.extend = function (other_array) {
    /* You should include a test to check whether other_array really is an array */
    other_array.forEach(function(v) {this.push(v)}, this);
}

然后,您可以执行以下操作:

var a = [1,2,3];
var b = [5,4,3];
a.extend(b);

.apply当我们附加的数组很大时,DzinX 的答案(使用 push.apply)和其他基于方法的方法会失败(测试表明,对我来说,Chrome 中大约有 > 150,000 个条目,Firefox 中大约有 > 500,000 个条目)。您可以在此 jsperf 中看到此错误

使用大数组作为第二个参数调用 'Function.prototype.apply' 时,由于超出了调用堆栈大小,因此会发生错误。(MDN 有关于使用Function.prototype.apply超过调用堆栈大小的危险的注释- 请参阅标题为“应用和内置函数”的部分。)

要与此页面上的其他答案进行速度比较,请查看此 jsperf(感谢 EaterOfCode)。基于循环的实现在速度上与 using 相似Array.push.apply,但往往比Array.slice.apply.

有趣的是,如果您附加的数组是稀疏的,则上述forEach基于方法可以利用稀疏性并优于.apply基于方法;如果您想亲自测试,请查看此 jsperf

顺便说一句,不要被诱惑(就像我一样!)将 forEach 实现进一步缩短为:

Array.prototype.extend = function (array) {
    array.forEach(this.push, this);
}

因为这会产生垃圾结果!为什么?因为Array.prototype.forEach为它调用的函数提供了三个参数 - 它们是:(element_value, element_index, source_array)。forEach如果您使用“forEach(this.push, this)”,所有这些都将在每次迭代时推送到您的第一个数组

@BenjaminGruenbaum - 你能发布一些结果的链接吗?从我在此评论末尾链接的 jsperf 收集的结果中可以看出,使用.forEach速度比.push.applyChrome/Chromium(自 v25 以来的所有版本)。我无法单独测试 v8,但如果您有,请链接您的结果。参见 jsperf:jsperf.com/array-extending-push-vs-concat/5
2021-03-12 23:51:37
ps 测试 other_array 是否真的是一个数组,选择这里描述的选项之一:stackoverflow.com/questions/767486/...
2021-03-20 23:51:37
好答案。我没有意识到这个问题。我在这里引用了您的答案:stackoverflow.com/a/4156156/96100
2021-03-23 23:51:37
@jcdude 您的 jsperf 无效。由于数组中的所有元素都是undefined,因此.forEach将跳过它们,使其成为最快的。.splice居然是最快的?!jsperf.com/array-extending-push-vs-concat/18
2021-03-26 23:51:37
.push.apply实际上比.forEachv8快得多,最快的仍然是内联循环。
2021-04-01 23:51:37

我觉得这几天最优雅的是:

arr1.push(...arr2);

关于扩展运算符MDN 文章在 ES2015 (ES6) 中提到了这种很好的方式:

更好的推动

示例:push 通常用于将数组推送到现有数组的末尾。在 ES5 中,这通常是这样完成的:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

在带有传播的 ES6 中,这变为:

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
arr1.push(...arr2);

请注意,arr2不能很大(保持在大约 100 000 项以下),因为根据 jcdude 的回答,调用堆栈会溢出。

这速度够快吗?
2021-03-22 23:51:37
使用时有一个方便之.concat处,即第二个参数不能是数组。这可以通过将扩展运算符与concat,例如arr1.push(...[].concat(arrayOrSingleItem))
2021-03-24 23:51:37
这是现代、优雅的 Javascript,适用于目标数组不为空的情况。
2021-03-30 23:51:37
只是对我刚刚犯的错误的警告。ES6 版本非常接近arr1.push(arr2). 这可能是一个问题arr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2),结果是一个数组数组arr1 == [['a','b','d']]而不是两个数组的组合。这是一个容易犯的错误。出于这个原因,我更喜欢下面的第二个答案。 stackoverflow.com/a/31521404/4808079
2021-04-07 23:51:37

首先说几句关于apply()在 JavaScript 中的内容,以帮助理解我们为什么使用它:

apply()方法调用具有给定this的函数,并以数组形式提供参数。

Push 需要将项目列表添加到数组中。apply()但是,方法将函数调用的预期参数作为数组。这使我们可以push使用内置push()方法轻松地将一个数组的元素转换为另一个数组

想象一下你有这些数组:

var a = [1, 2, 3, 4];
var b = [5, 6, 7];

只需这样做:

Array.prototype.push.apply(a, b);

结果将是:

a = [1, 2, 3, 4, 5, 6, 7];

同样的事情可以在 ES6 中使用扩展运算符 (" ...")来完成,如下所示:

a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7]; 

更短更好,但目前并非所有浏览器都完全支持。

此外,如果您想所有内容从 array移动bab在此过程中清空,您可以这样做:

while(b.length) {
  a.push(b.shift());
} 

结果如下:

a = [1, 2, 3, 4, 5, 6, 7];
b = [];
或者while (b.length) { a.push(b.shift()); },对吗?
2021-03-17 23:51:37