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

我想达到以下几点:

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

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

注意:这不是“如何将内容追加到数组?”这里的目标是将一个数组的全部内容添加到另一个数组中,并做到“就地”,即不复制扩展数组的所有元素。


push方法可以接受多个参数。你可以使用展开操作符将第二个数组的所有元素作为参数传递给.push:

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

如果你的浏览器不支持ECMAScript 6,你可以使用.apply来代替:

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

或者,如果你觉得更清楚的话:

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

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


可以使用splice()来实现:

b.unshift(b.length)
b.unshift(a.length)
Array.prototype.splice.apply(a,b) 
b.shift() // Restore b
b.shift() // 

但尽管它更丑,但并不比推快。至少在Firefox 3.0中不适用。


我喜欢推。应用上面描述的(a, b)方法,如果你想,你可以创建一个这样的库函数:

Array.prototype.append = function(array)
{
    this.push.apply(this, array)
}

像这样使用它

a = [1,2]
b = [3,4]

a.append(b)

您应该使用基于循环的技术。本页上基于.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);

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

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

要与本页上的其他答案进行速度比较,请查看这个jsperf(感谢EaterOfCode)。基于循环的实现在速度上与使用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(this。推动,这)”!


把答案结合起来……

Array.prototype.extend = function(array) {
    if (array.length < 150000) {
        this.push.apply(this, array)
    } else {
        for (var i = 0, len = array.length; i < len; ++i) {
            this.push(array[i]);
        };
    }  
}

如果你想使用jQuery,有$.merge()

例子:

a = [1, 2];
b = [3, 4, 5];
$.merge(a,b);

结果:a = [1,2,3,4,5]


2018年更新:更好的答案是我的一个更新的答案:A .push(…b)。不要再给这个投票了,因为它从来没有真正回答过这个问题,但它是2015年关于谷歌第一次点击的黑客攻击:)


对于那些仅仅搜索“JavaScript数组扩展”并到达这里的人来说,您可以很好地使用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];

(它也复制。)


你可以创建一个polyfill for extend,如下所示。它会添加到数组中;In-place并返回自身,这样就可以链接其他方法。

如果(Array.prototype。Extend === undefined) { Array.prototype.extend = function(other) { this.push。应用(这个参数。长度> 1 ?论据:其他); 返回; }; } 函数print() { document.body.innerHTML += [].map。调用(参数,函数(项){ 返回typeof item === 'object' ?JSON.stringify(item): item; })。Join (' ') + '\n'; } document.body.innerHTML = "; Var a = [1,2,3]; Var b = [4,5,6]; 打印(“Concat”); print(“(1)”,a.concat (b)); print(“(2)”,a.concat (b)); Print ('(3)', a.c concat(4,5,6)); 打印(“\ nExtend”); print(“(1)”,a.extend (b)); print(“(2)”,a.extend (b)); Print ('(3)', a.extend(4,5,6)); 身体{ 字体类型:等宽字体; 空白:前; }


答案非常简单。

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
(The following code will combine the two arrays.)

a = a.concat(b);

>>> a
[1, 2, 3, 4, 5]

Concat的作用与JavaScript字符串连接非常相似。它将返回您在调用函数的数组末尾放入concat函数的参数的组合。关键是你必须把返回值赋给一个变量,否则它就会丢失。例如,

a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it doesn't do anything with it.

我觉得最近最优雅的是:

arr1.push(...arr2);

MDN上关于扩展操作符的文章在ES2015 (ES6)中提到了这种漂亮的甜美方式:

更好的推动 示例:push通常用于将一个数组推入到现有数组的末尾 数组中。在ES5中,这通常是这样做的: Var arr1 = [0,1,2]; Var arr2 = [3,4,5]; //将arr2中的所有项追加到arr1 Array.prototype.push。应用(arr1 arr2); 在ES6中,这变成: Var arr1 = [0,1,2]; Var arr2 = [3,4,5]; arr1.push(…arr2);

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


合并两个以上数组的另一种解决方案

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

// Merge the contents of multiple arrays together into the first array
var mergeArrays = function() {
 var i, len = arguments.length;
 if (len > 1) {
  for (i = 1; i < len; i++) {
    arguments[0].push.apply(arguments[0], arguments[i]);
  }
 }
};

然后调用并打印为:

mergeArrays(a, b, c);
console.log(a)

输出将是:数组[1,2,3,4,5,6,7]


首先介绍一下JavaScript中的apply(),帮助我们理解为什么要使用它:

apply()方法调用具有给定此值的函数,并且 作为数组提供的参数。

Push需要一个要添加到数组的项列表。然而,apply()方法将函数调用的预期参数作为一个数组。这允许我们使用内置的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]; 

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

同样,如果你想把所有的东西从数组b移动到a,在这个过程中清空b,你可以这样做:

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

结果如下:

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

这个解决方案适用于我(使用ECMAScript 6的扩展操作符):

Let array = ['my', 'solution', 'works']; let newArray = []; let newArray2 = []; newArray.push数组(…);//添加到相同的数组 newArray2.push([…]数组);//添加为子/叶子/子数组 console.log (newArray); console.log (newArray2);


使用数组。extend而不是Array。推送> 150,000条记录。

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }

    for (let record of arr) {
      this.push(record);
    }

    return this;
  };
}

超级简单,不依赖扩展运算符或应用,如果这是一个问题。

b.map(x => a.push(x));

在运行了一些性能测试后,它非常慢,但回答了关于不创建新数组的问题。Concat的速度要快得多,甚至jQuery的$.merge()也不行。

https://jsperf.com/merge-arrays19b/1


只需在push()方法的帮助下向数组添加新元素就可以做到这一点。

let colors = ["Red", "Blue", "Orange"]; console.log(' push前数组:' + colors); //添加新值到数组 colors.push(“绿色”); console.log(' push后数组:' + colors);

另一个用于将元素追加到数组开头的方法是unshift()函数,它添加并返回新的长度。它接受多个参数,附加现有元素的索引,最后返回数组的新长度:

let colors = ["Red", "Blue", "Orange"]; console.log(' unshift前数组:' + colors); //添加新值到数组 颜色。未(“黑色”,“绿色”); console.log(' unshift后的数组:' + colors);

还有其他方法。你可以在这里查看。


概述

A.push(…b) -有限的,快速的,现代语法 a.push。应用(a, b) -有限,快速 A = a.c concat(b)无限,如果A很大则很慢 For (let I in b) {a push(b[I]);} -无限,如果b很大则很慢

每个代码片段将a修改为用b扩展。

“limited”代码段将每个数组元素作为参数传递,并且可以传递给函数的参数的最大数量是有限的。从这个链接来看,a.push(…b)似乎是可靠的,直到b中有大约32k个元素(a的大小无关紧要)。

相关MDN文档:spread语法,.apply(), .concat(), .push()

速度的考虑

如果a和b都很小,那么每个方法都是快速的,所以在大多数web应用程序中,你会希望使用push(…b)并完成它。

如果你要处理超过几千个元素,你想要做什么取决于情况:

您正在向一个大数组中添加一些元素 →push(…b)非常快 您正在向一个大数组中添加许多元素 →concat比循环略快 您正在向一个小数组中添加许多元素 →concat比循环快得多 您通常只向任何大小的数组添加少量元素 →循环的速度与用于少量添加的有限方法一样快,但即使在添加许多元素时性能不是最好的,它也不会抛出异常 您正在编写一个包装器函数以始终获得最大性能 →你需要动态检查输入的长度并选择正确的方法,也许在循环中调用push(…b_part)(带有大b的切片)。

这让我很惊讶:我认为a=a.concat(b)将能够在a上对b进行很好的memcpy,而不需要像a.push(…b)那样做单独的扩展操作,因此总是最快的。相反,a.push(…b)要快得多,特别是当a很大的时候。

在Linux下的Firefox 88中,我们使用以下方法来测量不同方法的速度:

a = [];
for (let i = 0; i < Asize; i++){
  a.push(i);
}
b = [];
for (let i = 0; i < Bsize; i++){
  b.push({something: i});
}
t=performance.now();
// Code to test
console.log(performance.now() - t);

参数及结果:

 ms | Asize | Bsize | code
----+-------+-------+------------------------------
 ~0 |  any  |  any  | a.push(...b)
 ~0 |  any  |  any  | a.push.apply(a, b)
480 |   10M |    50 | a = a.concat(b)
  0 |   10M |    50 | for (let i in b) a.push(b[i])
506 |   10M |  500k | a = a.concat(b)
882 |   10M |  500k | for (let i in b) a.push(b[i])
 11 |    10 |  500k | a = a.concat(b)
851 |    10 |  500k | for (let i in b) a.push(b[i])

请注意,Bsize为500000是我的系统上所有方法所接受的最大值,这就是为什么它小于Asize。

所有测试都运行了多次,以查看结果是异常值还是具有代表性。当然,使用performance.now()在一次运行中几乎无法测量快速方法,但是由于慢方法是如此明显,而且两个快速方法都工作在相同的原理上,因此我们不需要麻烦地重复它一堆次来区分细节。

如果任意一个数组都很大,concat方法总是很慢,但只有当它必须执行大量函数调用并且不关心a有多大时,循环才会很慢。因此,循环类似于push(…b)或push。申请小的bs,但如果确实变大了,不要中断;但是,当您接近极限时,concat又快了一点。


另一种选择,如果你安装了lodash:

 import { merge } from 'lodash';

 var arr1 = merge(arr1, arr2);

我添加了这个答案,因为尽管这个问题没有创建一个新数组,但几乎每个答案都忽略了它。

现代JavaScript可以很好地处理数组等可迭代对象。这使得在此基础上实现一个版本的concat成为可能,并在逻辑上跨越数组数据的参数。

下面的例子使用了iter-ops库,它具有这样的逻辑:

import {pipe, concat} from 'iter-ops';

const i = pipe(
    originalArray,
    concat(array2, array3, array4, ...)
); //=> Iterable

for(const a of i) {
    console.log(a); // iterate over values from all arrays
}

上面没有创建新数组。操作符concat将遍历原始数组,然后按照指定的顺序自动继续到array2、array3等等。

就内存使用而言,这是最有效的连接数组的方法。

如果在最后,你决定将它转换为一个实际的物理数组,你可以通过展开操作符或array .from来实现:

const fullArray1 = [...i]; // pulls all values from iterable, into a new array

const fullArray2 = Array.from(i); // does the same