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

时间:2009-09-03 15:27:31

标签: javascript arrays concatenation

似乎没有办法用另一个数组扩展现有的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?的重复 - 这里的目标是添加一个数组的全部内容到另一个,并“在适当的位置”,即不复制扩展数组的所有元素。

19 个答案:

答案 0 :(得分:1264)

.push方法可以使用多个参数。您可以使用spread operator将第二个数组的所有元素作为参数传递给.push

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

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

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

或许,如果你认为它更清楚:

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

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

答案 1 :(得分:233)

更新2018年A better answer is a newer one of minea.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];

(它也复制。)

答案 2 :(得分:188)

您应该使用基于循环的技术。此页面上基于使用.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's answer(使用push.apply)和其他基于.apply的方法失败(测试显示,对于我来说,大约在Chrome中有大约150,000个条目,并且> Firefox中的500,000个条目。您可以在this jsperf中看到此错误。

发生错误是因为当使用大数组作为第二个参数调用'Function.prototype.apply'时,超出了调用堆栈大小。 (MDN记录了使用Function.prototype.apply超出调用堆栈大小的危险 - 请参阅标题为“应用和内置函数”的部分。)

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

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

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

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

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

答案 3 :(得分:145)

我觉得这些日子里最优雅的是:

arr1.push(...arr2);

MDN article on the spread operator在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不是很大(保持在大约10万项以下),因为调用堆栈溢出,根据jcdude的答案。

答案 4 :(得分:46)

首先在JavaScript中提及apply()以帮助理解我们使用它的原因:

  

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

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

此外,如果你想移动从数组ba的所有内容,在此过程中清空b,你可以这样做:

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

,结果如下:

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

答案 5 :(得分:38)

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

示例:

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

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

答案 6 :(得分:19)

我喜欢上面描述的a.push.apply(a, b)方法,如果你愿意,你总是可以像这样创建一个库函数:

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

并像这样使用

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

a.append(b)

答案 7 :(得分:13)

可以使用splice()

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

但是尽管它更加丑陋但它并不比push.apply快,至少在Firefox 3.0中没有。

答案 8 :(得分:4)

此解决方案对我有用(使用ECMAScript 6的传播算子):

let array = ['my', 'solution', 'works'];
let newArray = [];
let newArray2 = [];
newArray.push(...array); // Adding to same array
newArray2.push([...array]); // Adding as child/leaf/sub-array
console.log(newArray);
console.log(newArray2);

答案 9 :(得分:4)

概述

  • a.push(...b) - 有限、快速、现代的语法
  • a.push.apply(a, b) - 有限、快速
  • a = a.concat(b) 无限制,如果 a 很大则慢
  • for (let i in b) { a.push(b[i]); } - 无限制,如果 b 很大,则缓慢

每个片段都会修改 a 以扩展为 b

“受限”片段将每个数组元素作为参数传递,the maximum number of arguments you can pass to a function is limited。从该链接看来,a.push(...b) 似乎是可靠的,直到 b 中有大约 32k 个元素(a 的大小无关紧要)。

相关 MDN 文档:spread syntax.apply().concat().push()

速度注意事项

如果 ab 都很小,那么每种方法都很快,因此在大多数 Web 应用程序中,您会希望使用 push(...b) 并用它完成。

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

  • 您正在向一个大数组中添加一些元素
    push(...b) 非常快
  • 您正在向一个大数组中添加许多元素
    concat 比循环稍快
  • 您正在向一个小数组中添加许多元素
    concat 比循环快得多

这让我感到惊讶:我认为 a=a.concat(b) 可以在 b 上执行 a 的不错的 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 为 500 000 是我系统上所有方法可接受的最大值,这就是它小于 Asize 的原因。

所有测试都运行了多次,以查看结果是否为异常值或具有代表性。当然,快速方法在使用 performance.now() 的一次运行中几乎是不可估量的,但是由于慢速方法是如此明显并且两种快速方法都以相同的原理工作,我们不必费心重复一堆分头发的次数。

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

答案 10 :(得分:2)

您可以创建一个polyfill for extend,如下所示。它会添加到数组中;就地并返回自己,以便您可以链接其他方法。

&#13;
&#13;
if (Array.prototype.extend === undefined) {
  Array.prototype.extend = function(other) {
    this.push.apply(this, arguments.length > 1 ? arguments : other);
    return this;
  };
}

function print() {
  document.body.innerHTML += [].map.call(arguments, function(item) {
    return typeof item === 'object' ? JSON.stringify(item) : item;
  }).join(' ') + '\n';
}
document.body.innerHTML = '';

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

print('Concat');
print('(1)', a.concat(b));
print('(2)', a.concat(b));
print('(3)', a.concat(4, 5, 6));

print('\nExtend');
print('(1)', a.extend(b));
print('(2)', a.extend(b));
print('(3)', a.extend(4, 5, 6));
&#13;
body {
  font-family: monospace;
  white-space: pre;
}
&#13;
&#13;
&#13;

答案 11 :(得分:1)

我可以看到非常好的答案,这完全取决于您的阵列大小,您的要求和目标。它可以以不同的方式完成。

我建议使用JavaScript for循环:

temp.myContent = CONTENT
temp.myContent {
    table = tt_content
    select {
        begin = 1
        orderBy = sorting
        where = (colPos = 2)
    }
    renderObj = COA 
    renderObj {

        # PLANNING TO PLACE THE CONDITIONS SOMEWHERE HERE

        # 10 = TEXT  
        # 10 {
             ...
        # }
    }
}
输出

var a = [1, 2]; var b = [3, 4, 5]; for (i = 0; i < b.length; i++) { a.push(b[i]); }

console.log(a)

然后你可以自己动手:

Array [ 1, 2, 3, 4, 5 ]
输出

function extendArray(a, b){ for (i = 0; i < b.length; i++) { a.push(b[i]); } return a; }

console.log(extendArray(a, b));

答案 12 :(得分:0)

结合答案......

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

答案 13 :(得分:0)

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

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)

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

答案 14 :(得分:0)

使用for i in xrange(len(testlist)): if testlist[i] == 2: print i代替Array.extend进行150,000条以上的记录。

Array.push

答案 15 :(得分:-1)

另一个选项,如果你安装了 lodash:

 import { merge } from 'lodash';

 var arr1 = merge(arr1, arr2);

答案 16 :(得分:-1)

答案非常简单。

>>> 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.

答案 17 :(得分:-3)

您可以通过在push()方法的帮助下将新元素简单地添加到数组中来实现。

let colors = ["Red", "Blue", "Orange"];
console.log('Array before push: ' + colors);
// append new value to the array
colors.push("Green");
console.log('Array after push : ' + colors);

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

let colors = ["Red", "Blue", "Orange"];
console.log('Array before unshift: ' + colors);
// append new value to the array
colors.unshift("Black", "Green");
console.log('Array after unshift : ' + colors);

还有其他方法。您可以检出它们here

答案 18 :(得分:-5)

超级简单,如果有问题,就不用依靠散布算子或套用。

public boolean answerCell(boolean isMorning, boolean isMom, boolean isAsleep){
    return isAsleep?false:isMorning?isMom:true;
}

对此进行了一些性能测试之后,它的运行速度非常慢,但是回答了有关不创建新阵列的问题。 Concat更快,甚至jQuery的 <%= f.fields_for :questions do |question_form| %> <%= question_form.text_field :question_text %> <%= question_form.select(:question_type, [ 'TEXT', 'OPTIONS', 'UPLOAD' ],{:prompt => 'Select One'}, :id => "my_id", :onchange => "myFunction()") %> <%= question_form.link_to_remove "Remove this Question" %> <%= question_form.fields_for :options do |option_form| %> <%= option_form.text_field :option_text %> <%= option_form.link_to_remove "Remove this option" %> <% end %> <p id = "test" hidden><%= question_form.link_to_add "Add a option", :options %></p> <% end %> <p><%= f.link_to_add "Add a Question", :questions %></p> <p> <%= f.submit %> </p> <% end %> <script> function myFunction(){ var e = document.getElementById("my_id"); var x = e.options[e.selectedIndex].value if (x == "OPTIONS"){ document.getElementById("test").hidden = false; } else{ document.getElementById("test").hidden = true; } } </script>都快。

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