将数组项复制到另一个数组中

我有一个JavaScript数组dataArray,我想将其推送到一个新数组newArray中。除了我不希望newArray[0]dataArray。我想将所有项目推送到新数组中:

var newArray = [];
newArray.pushValues(dataArray1);newArray.pushValues(dataArray2);// ...

甚至更好:

var newArray = new Array (dataArray1.values(),dataArray2.values(),// ... where values() (or something equivalent) would push the individual values into the array, rather than the array itself);

所以现在新数组包含各个数据数组的所有值。是否有一些速记方式,如pushValues可用,这样我就不必遍历每个单独的dataArray,逐个添加项目?

1519156 次浏览
var a=new Array('a','b','c');var b=new Array('d','e','f');var d=new Array('x','y','z');var c=a.concat(b,d)

这能解决你的问题吗?

使用conat函数,如下所示:

var arrayA = [1, 2];var arrayB = [3, 4];var newArray = arrayA.concat(arrayB);

newArray的值将是[1, 2, 3, 4]arrayAarrayB保持不变;concat为结果创建并返回一个新数组)。

如果你的数组不是很大(请参阅下面的警告),你可以使用你希望附加值的数组的push()方法。push()可以接受多个参数,所以你可以使用它的apply()方法来传递要作为函数参数列表推送的值数组。这比使用concat()的优点是将元素添加到数组中而不是创建一个新数组。

但是,对于大型数组(大约100,000个成员或更多),似乎这个把戏可能会失败。对于此类数组,使用循环是更好的方法。有关详细信息,请参阅https://stackoverflow.com/a/17368101/96100

var newArray = [];newArray.push.apply(newArray, dataArray1);newArray.push.apply(newArray, dataArray2);

你可能想把它推广到一个函数中:

function pushArray(arr, arr2) {arr.push.apply(arr, arr2);}

…或将其添加到Array的原型中:

Array.prototype.pushArray = function(arr) {this.push.apply(this, arr);};
var newArray = [];newArray.pushArray(dataArray1);newArray.pushArray(dataArray2);

…或者通过允许多个参数来模拟原始的push()方法,使用concat()push()一样允许多个参数的事实:

Array.prototype.pushArray = function() {this.push.apply(this, this.concat.apply([], arguments));};
var newArray = [];newArray.pushArray(dataArray1, dataArray2);

这是最后一个示例的基于循环的版本,适用于大型数组和所有主要浏览器,包括IE<=8:

Array.prototype.pushArray = function() {var toPush = this.concat.apply([], arguments);for (var i = 0, len = toPush.length; i < len; ++i) {this.push(toPush[i]);}};

代替ush()函数,对IE.使用concat函数。例如,

var a=a.concat(a,new Array('amin'));

Thes是一个工作代码,它运行良好:

var els = document.getElementsByTagName('input'), i;var invnum = new Array();var k = els.length;for(i = 0; i < k; i++){invnum.push(new Array(els[i].id,els[i].value))}

在ECMAScript 6中,您可以使用传播语法

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

扩展语法在所有主要浏览器中都可用(不包括IE11)。有关当前兼容性,请参阅此(不断更新)兼容性表

但是,请参阅下面Jack Giffin的回复以获取更多关于性能的评论。似乎concat仍然比传播运算符更好更快。

MDN找到优雅的方式

var vegetables = ['parsnip', 'potato'];var moreVegs = ['celery', 'beetroot'];
// Merge the second array into the first one// Equivalent to vegetables.push('celery', 'beetroot');Array.prototype.push.apply(vegetables, moreVegs);
console.log(vegetables); // ['parsnip', 'potato', 'celery', 'beetroot']

或者你可以使用ES6的spread operator特性:

let fruits = [ 'apple', 'banana'];const moreFruits = [ 'orange', 'plum' ];
fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]

以下对我来说似乎是最简单的:

var newArray = dataArray1.slice();newArray.push.apply(newArray, dataArray2);

由于“推送”接受可变数量的参数,您可以使用push函数的apply方法推送另一个数组的所有元素。它构造使用其第一个参数(这里的“newArray”)作为“this”和数组的元素作为剩余的参数。

第一条语句中的slice获取第一个数组的副本,因此您不会修改它。

更新如果您使用的是可用切片的javascript版本,您可以将push表达式简化为:

newArray.push(...dataArray2)

使用JavaScript ES6,您可以使用…运算符作为扩展运算符,它将基本上将数组转换为值。然后,您可以这样做:

const myArray = [1,2,3,4,5];const moreData = [6,7,8,9,10];
const newArray = [...myArray,...moreData,];

虽然语法很简洁,但我不知道它在内部是如何工作的,以及对大型数组的性能影响是什么。

下面的函数对数组的长度没有问题,并且比所有建议的解决方案都要好:

function pushArray(list, other) {var len = other.length;var start = list.length;list.length = start + len;for (var i = 0; i < len; i++ , start++) {list[start] = other[i];}}

不幸的是,jspref拒绝接受我的提交,所以这里是使用benchmark.js的结果

        Name            |   ops/sec   |  ± %  | runs sampledfor loop and push       |      177506 |  0.92 | 63Push Apply              |      234280 |  0.77 | 66spread operator         |      259725 |  0.40 | 67set length and for loop |      284223 |  0.41 | 66

在哪里

for循环和推送是:

    for (var i = 0, l = source.length; i < l; i++) {target.push(source[i]);}

推送申请:

target.push.apply(target, source);

点差运算符:

    target.push(...source);

最后,'set长度和for循环'就是上面的函数

我们有两个数组a和b。这里的代码是数组a值被推送到数组b中。

let a = [2, 4, 6, 8, 9, 15]
function transform(a) {let b = ['4', '16', '64']a.forEach(function(e) {b.push(e.toString());});return b;}
transform(a)
[ '4', '16', '64', '2', '4', '6', '8', '9', '15' ]

有很多关于Array.prototype.push.apply的答案。这是一个明显的例子:

var dataArray1 = [1, 2];var dataArray2 = [3, 4, 5];var newArray = [ ];Array.prototype.push.apply(newArray, dataArray1); // newArray = [1, 2]Array.prototype.push.apply(newArray, dataArray2); // newArray = [1, 2, 3, 4, 5]console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

如果您有ES6语法:

var dataArray1 = [1, 2];var dataArray2 = [3, 4, 5];var newArray = [ ];newArray.push(...dataArray1); // newArray = [1, 2]newArray.push(...dataArray2); // newArray = [1, 2, 3, 4, 5]console.log(JSON.stringify(newArray)); // Outputs: [1, 2, 3, 4, 5]

这是ES6的方式

var newArray = [];let dataArray1 = [1,2,3,4]let dataArray2 = [5,6,7,8]newArray = [...dataArray1, ...dataArray2]console.log(newArray)

上述方法适用于大多数情况,而不是请考虑concat的情况,例如数组中有数十万个项目。

    let dataArray1 = [1,2,3,4]let dataArray2 = [5,6,7,8]let newArray = dataArray1.concat(dataArray2);console.log(newArray)

𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵 𝗔𝗻𝗱 𝗕𝗲𝗻𝗰𝗵𝗺𝗮𝗿𝗸𝘀 𝗣𝗼𝘄𝗲𝗿𝗳𝘂𝗹𝗹𝘆 𝗦𝗵𝗼𝘄 𝗧𝗵𝗮𝘁 𝗖𝗼𝗻𝗰𝗮𝘁 𝗜𝘀 𝗧𝗵𝗲 𝗕𝗲𝘀𝘁 (原问题)

对于事实,执行性能测试并检查控制台中的一些内容。对于研究,使用网站irt.org。下面是所有这些来源的集合,以及底部的示例函数。

╔═══════════════╦══════╦═════════════════╦═══════════════╦═════════╦══════════╗║ Method        ║Concat║slice&push.apply ║ push.apply x2 ║ ForLoop ║Spread    ║╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣║ mOps/Sec      ║179   ║104              ║ 76            ║ 81      ║28        ║╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣║ Sparse arrays ║YES!  ║Only the sliced  ║ no            ║ Maybe2   ║no        ║║ kept sparse   ║      ║array (1st arg)  ║               ║         ║          ║╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣║ Support       ║MSIE 4║MSIE 5.5         ║ MSIE 5.5      ║ MSIE 4  ║Edge 12   ║║ (source)      ║NNav 4║NNav 4.06        ║ NNav 4.06     ║ NNav 3  ║MSIE NNav ║╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣║Array-like acts║no    ║Only the pushed  ║ YES!          ║ YES!    ║If have   ║║like an array  ║      ║array (2nd arg)  ║               ║         ║iterator1  ║╚═══════════════╩══════╩═════════════════╩═══════════════╩═════════╩══════════╝1 If the array-like object does not have a Symbol.iterator property, then tryingto spread it will throw an exception.2 Depends on the code. The following example code "YES" preserves sparseness.
function mergeCopyTogether(inputOne, inputTwo){var oneLen = inputOne.length, twoLen = inputTwo.length;var newArr = [], newLen = newArr.length = oneLen + twoLen;for (var i=0, tmp=inputOne[0]; i !== oneLen; ++i) {tmp = inputOne[i];if (tmp !== undefined || inputOne.hasOwnProperty(i)) newArr[i] = tmp;}for (var two=0; i !== newLen; ++i, ++two) {tmp = inputTwo[two];if (tmp !== undefined || inputTwo.hasOwnProperty(two)) newArr[i] = tmp;}return newArr;}

如上所述,我认为Concat几乎总是既能提高性能又能保持备用数组的稀疏性的方法。然后,对于类似数组(例如像document.body.children这样的DOMNodeList),我建议使用for循环,因为它既是第二高性能的方法,也是唯一保留稀疏数组的其他方法。下面,我们将快速介绍稀疏数组和数组喜欢的含义,以消除混淆。

𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲

起初,有些人可能认为这是侥幸,浏览器供应商最终会绕过优化Array.prototype.push,以足够快地击败Array.prototype.concat.错了!Array.prototype.concat总是会更快(至少在原则上),因为它是对数据的简单复制-粘贴。下面是一个简化的32位数组实现可能是什么样子的说服可视化图(请注意,真正的实现要复杂得多)

Byte ║ Data here═════╬═══════════0x00 ║ int nonNumericPropertiesLength = 0x000000000x01 ║ ibid0x02 ║ ibid0x03 ║ ibid0x00 ║ int length = 0x000000010x01 ║ ibid0x02 ║ ibid0x03 ║ ibid0x00 ║ int valueIndex = 0x000000000x01 ║ ibid0x02 ║ ibid0x03 ║ ibid0x00 ║ int valueType = JS_PRIMITIVE_NUMBER0x01 ║ ibid0x02 ║ ibid0x03 ║ ibid0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (or whereever it is in memory)0x01 ║ ibid0x02 ║ ibid0x03 ║ ibid

如上所述,复制类似的内容几乎就像复制一个字节一个字节一样简单。使用Array.prototype.push.apply,它不仅仅是简单的复制-粘贴数据。“.应用”必须检查数组中的每个索引,并将其转换为一组参数,然后再将其传递给Array.prototype.push.然后,Array.prototype.push每次都必须额外分配更多内存,并且(对于某些浏览器实现)甚至可能重新计算一些位置查找数据以实现稀疏性。

另一种思考方式是这样的。源数组一是一大堆文件装订在一起。源数组二也是另一大堆文件。你会更快地

  1. 去商店,购买足够的纸张来复制每个源数组。然后将每个源数组堆叠的纸张通过复印机并将生成的两个副本钉在一起。
  2. 去商店,购买足够的纸张来复制第一个源数组的单个副本。然后,手工将源数组复制到新纸张上,确保填写任何空白稀疏点。然后,回到商店,购买足够的纸张用于第二个源数组。然后,通过第二个源数组并复制它,同时确保副本中没有空白空白。然后,将所有复制的纸张钉在一起。

在上面的类比中,选项#1代表Array.prototype.concat而#2代表Array.prototype.push.apply.让我们用类似的JSperf测试一下,不同之处在于这个测试稀疏数组上的方法,而不是固体数组。人们可以找到它就在这里

因此,我认为这个特定用例的性能未来不在于Array.prototype.push,而在于Array.prototype.concat.

𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀

𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀

当数组的某些成员丢失时。例如:

// This is just as an example. In actual code,// do not mix different types like this.var mySparseArray = [];mySparseArray[0] = "foo";mySparseArray[10] = undefined;mySparseArray[11] = {};mySparseArray[12] =  10;mySparseArray[17] = "bar";console.log("Length:   ", mySparseArray.length);console.log("0 in it:  ", 0 in mySparseArray);console.log("arr[0]:   ", mySparseArray[0]);console.log("10 in it: ", 10 in mySparseArray);console.log("arr[10]   ", mySparseArray[10]);console.log("20 in it: ", 20 in mySparseArray);console.log("arr[20]:  ", mySparseArray[20]);

或者,javascript允许您轻松初始化备用数组。

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];

𝗔𝗿𝗿𝗮𝘆-𝗟𝗶𝗸𝗲𝘀

类数组是至少具有length属性但未使用new Array[]初始化的对象;例如,以下对象被归类为类数组。

{0: "foo", 1: "bar", length:2}
document.body.children
new Uint8Array(3)
  • This is array-like because although it's a(n) (typed) array, coercing it to an array changes the constructor.
(function(){return arguments})()

观察使用将数组类型强制转换为切片等数组的方法会发生什么。

var slice = Array.prototype.slice;// For arrays:console.log(slice.call(["not an array-like, rather a real array"]));// For array-likes:console.log(slice.call({0: "foo", 1: "bar", length:2}));console.log(slice.call(document.body.children));console.log(slice.call(new Uint8Array(3)));console.log(slice.call( function(){return arguments}() ));

  • 注:由于性能原因,在函数参数上调用切片是不好的做法。

观察使用没有将array-like强制转换为concat之类的数组的方法会发生什么。

var empty = [];// For arrays:console.log(empty.concat(["not an array-like, rather a real array"]));// For array-likes:console.log(empty.concat({0: "foo", 1: "bar", length:2}));console.log(empty.concat(document.body.children));console.log(empty.concat(new Uint8Array(3)));console.log(empty.concat( function(){return arguments}() ));

如果要修改原始数组,可以传播推送:

var source = [1, 2, 3];var range = [5, 6, 7];var length = source.push(...range);
console.log(source); // [ 1, 2, 3, 5, 6, 7 ]console.log(length); // 6

如果您想确保只有相同类型的项目进入source数组(例如,不要混合数字和字符串),请使用TypeScript。

/*** Adds the items of the specified range array to the end of the source array.* Use this function to make sure only items of the same type go in the source array.*/function addRange<T>(source: T[], range: T[]) {source.push(...range);}

试试这个:

var arrayA = [1, 2];var arrayB = [3, 4];var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);console.log(newArray)

性能

我分析了当前的解决方案,并提出了2个新的解决方案(F和G在详细部分中介绍),其中一个对于中小型阵列来说非常快

今天2020.11.13我在Chromev86,Safariv13.1.2和Firefox v82上对MacO HighSierra 10.13.6进行测试,以选择解决方案

搜索结果

对于所有浏览器

  • 对于中小型数组,基于while-pop-unshift(F, G)的解决方案在所有浏览器上(非常)最快。对于具有50000个元素的数组,此解决方案会减慢Chrome
  • 数组500000中断的解决方案C、D:“RangeError:超出了最大调用堆栈大小
  • 解(E)最慢

在此处输入图片描述

详情

我执行两个测试用例:

  • 当数组有10个元素时-您可以运行它这里
  • 当数组有10k元素时-你可以运行它这里

下面的代码片段显示了解决方案之间的差异一个BCDE,F(my),G(我的)H

// https://stackoverflow.com/a/4156145/860099function A(a,b) {return a.concat(b);}
// https://stackoverflow.com/a/38107399/860099function B(a,b) {return [...a, ...b];}
// https://stackoverflow.com/a/32511679/860099function C(a,b) {return (a.push(...b), a);}
// https://stackoverflow.com/a/4156156/860099function D(a,b) {Array.prototype.push.apply(a, b);return a;}
// https://stackoverflow.com/a/60276098/860099function E(a,b) {return b.reduce((pre, cur) => [...pre, cur], a);}
// myfunction F(a,b) {while(b.length) a.push(b.shift());return a;}
// myfunction G(a,b) {while(a.length) b.unshift(a.pop());return b;}
// https://stackoverflow.com/a/44087401/860099function H(a, b) {var len = b.length;var start = a.length;a.length = start + len;for (var i = 0; i < len; i++ , start++) {a[start] = b[i];}return a;}
// https://stackoverflow.com/a/51860949/860099function I(a, b){var oneLen = a.length, twoLen = b.length;var newArr = [], newLen = newArr.length = oneLen + twoLen;for (var i=0, tmp=a[0]; i !== oneLen; ++i) {tmp = a[i];if (tmp !== undefined || a.hasOwnProperty(i)) newArr[i] = tmp;}for (var two=0; i !== newLen; ++i, ++two) {tmp = b[two];if (tmp !== undefined || b.hasOwnProperty(two)) newArr[i] = tmp;}return newArr;}





// ---------// TEST// ---------
let a1=[1,2,3];let a2=[4,5,6];
[A,B,C,D,E,F,G,H,I].forEach(f=> {console.log(`${f.name}: ${f([...a1],[...a2])}`)})

以下是chrome的示例结果

在此处输入图片描述

    public static void main(String[] args) {// TODO Auto-generated method stub//Scanner sc=new Scanner(System.in);    
int[] ij= {1,4,222,455,111};int[] ijk=Arrays.copyOf(ij,ij.length);for(int i=0;i<ij.length;i++) {System.out.print(i);}System.out.println(" ");for(int i=0;i<ijk.length;i++) {System.out.print(i);}    
    
}

输出:0123401234

我提供了两种简单的方法来做到这一点:

解决方案1:

let dataArray1= [0, 1];let dataArray1= [2, 3];dataArray1.push(...dataArray2);console.log(dataArray1)

解决方案2:

let dataArray1 = [1, 2];let dataArray2 = [3, 4];let newArray = dataArray1.concat(dataArray2);console.log(newArray)