Copy array items into another array
我有一个javascript数组
1 2 3 4 5 | var newArray = []; newArray.pushValues(dataArray1); newArray.pushValues(dataArray2); // ... |
甚至更好:
1 2 3 4 5 | 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 ); |
所以现在新数组包含了各个数据数组的所有值。是否有一些像
使用concat函数,如下所示:
1 2 3 | var arrayA = [1, 2]; var arrayB = [3, 4]; var newArray = arrayA.concat(arrayB); |
如果数组不是很大(请参见下面的警告),则可以使用希望向其追加值的数组的
然而,对于大型阵列(约100000个成员或更多),这个技巧可能会失败。对于这种数组,使用循环是一种更好的方法。有关详细信息,请参阅https://stackoverflow.com/a/17368101/96100。
1 2 3 | var newArray = []; newArray.push.apply(newArray, dataArray1); newArray.push.apply(newArray, dataArray2); |
您可能希望将其归纳为一个函数:
1 2 3 | function pushArray(arr, arr2) { arr.push.apply(arr, arr2); } |
…或者添加到
1 2 3 4 5 6 7 | Array.prototype.pushArray = function(arr) { this.push.apply(this, arr); }; var newArray = []; newArray.pushArray(dataArray1); newArray.pushArray(dataArray2); |
…或者通过允许多个参数来模拟原始的
1 2 3 4 5 6 | Array.prototype.pushArray = function() { this.push.apply(this, this.concat.apply([], arguments)); }; var newArray = []; newArray.pushArray(dataArray1, dataArray2); |
下面是上一个示例的基于循环的版本,适用于大型数组和所有主要浏览器,包括ie<=8:
1 2 3 4 5 6 | Array.prototype.pushArray = function() { var toPush = this.concat.apply([], arguments); for (var i = 0, len = toPush.length; i < len; ++i) { this.push(toPush[i]); } }; |
我再加一个"未来证明"回复
在ECMAScript 6中,可以使用排列运算符:
1 2 3 | var arr1 = [0, 1, 2]; var arr2 = [3, 4, 5]; arr1.push(...arr2); |
Spread运算符尚未包含在所有主要浏览器中。有关当前兼容性,请参阅此(持续更新的)兼容性表。
但是,可以将spread operator与babel.js一起使用。
编辑:
关于绩效的更多评论,请参阅下面的杰克·吉芬回复。看起来concat比spread操作符更好更快。
从MDN找到了一种优雅的方式
1 2 3 4 5 6 7 8 | 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的
1 2 3 4 | let fruits = [ 'apple', 'banana']; const moreFruits = [ 'orange', 'plum' ]; fruits.push(...moreFruits); // ["apple","banana","orange","plum"] |
以下对我来说似乎最简单:
1 2 | var newArray = dataArray1.slice(); newArray.push.apply(newArray, dataArray2); |
由于"push"采用的参数数量可变,因此可以使用
第一条语句中的
更新如果使用的是具有可用切片的javascript版本,则可以将
1 | newArray.push(...dataArray2) |
1 2 3 4 | 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) |
那能解决你的问题吗?
关于array.prototype.push.apply有很多答案。下面是一个明确的例子:
1 2 3 4 5 6 | 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语法:
1 2 3 4 5 6 | 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] |
下面的函数不存在数组长度问题,并且性能优于所有建议的解决方案:
1 2 3 4 5 6 7 8 | 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的结果。
1 2 3 4 5 | Name | ops/sec | ± % | runs sampled for loop and push | 177506 | 0.92 | 63 Push Apply | 234280 | 0.77 | 66 spread operator | 259725 | 0.40 | 67 set length and for loop | 284223 | 0.41 | 66 |
在哪里?
对于循环和推送:
1 2 3 | for (var i = 0, l = source.length; i < l; i++) { target.push(source[i]); } |
推应用:
1 | target.push.apply(target, source); |
排列运算符:
1 | target.push(...source); |
最后,"设置长度和for循环"是上面的函数
使用JavaScriptES6,您可以使用…运算符作为一个扩散运算符,它基本上将数组转换为值。然后,您可以这样做:
1 2 3 4 5 6 7 | const myArray = [1,2,3,4,5]; const moreData = [6,7,8,9,10]; const newArray = [ ...myArray, ...moreData, ]; |
虽然语法很简洁,但我不知道这在内部是如何工作的,也不知道大型数组的性能影响是什么。
研究和结果
事实上,在JSPerf执行性能测试,并在控制台中检查一些东西。研究使用网站irt.org。下面是所有这些源的集合,加上底部的示例函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | ╔═══════════════╦══════╦═════════════════╦═══════════════╦═════════╦══════════╗ ║ 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<sub> </sub> ║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 ║<strike>MSIE</strike> <strike>NNav</strike> ║ ╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣ ║Array-like acts║no ║Only the pushed ║ YES! ║ YES! ║If have ║ ║like an array ║ ║array (2nd arg) ║ ║ ║iterator1<sub> </sub>║ ╚═══════════════╩══════╩═════════════════╩═══════════════╩═════════╩══════════╝ 1 If the array-like object does not have a Symbol.iterator property, then trying to spread it will throw an exception. 2 Depends on the code. The following example code"YES" preserves sparseness. |
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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几乎总是性能和保留备用阵列稀疏性的方法。然后,对于类似数组(如domnodelist,如
起初,有些人可能认为这是一次侥幸,浏览器供应商最终将着手优化array.prototype.push,使其速度足以击败array.prototype.concat。错了!array.prototype.concat总是更快(原则上至少更快),因为它是一个简单的复制-粘贴数据。下面是一个简化的关于32位数组实现的Persuado可视图(请注意,实际的实现要复杂得多)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | Byte ║ Data here ═════╬═══════════ 0x00 ║ int nonNumericPropertiesLength = 0x00000000 0x01 ║ ibid 0x02 ║ ibid 0x03 ║ ibid 0x00 ║ int length = 0x00000001 0x01 ║ ibid 0x02 ║ ibid 0x03 ║ ibid 0x00 ║ int valueIndex = 0x00000000 0x01 ║ ibid 0x02 ║ ibid 0x03 ║ ibid 0x00 ║ int valueType = JS_PRIMITIVE_NUMBER 0x01 ║ ibid 0x02 ║ ibid 0x03 ║ ibid 0x00 ║ uintptr_t valuePointer = 0x38d9eb60 (or whereever it is in memory) 0x01 ║ ibid 0x02 ║ ibid 0x03 ║ ibid |
如上所述,复制类似的东西所需要做的一切几乎和逐字节复制一样简单。对于array.prototype.push.apply,它远不止是在数据上简单地复制粘贴。".apply"必须检查数组中的每个索引,并在将其传递给array.prototype.push之前将其转换为一组参数。然后,array.prototype.push每次都必须额外分配更多的内存,并且(对于某些浏览器实现)甚至可能重新计算一些位置查找数据的稀疏性。
另一种思考方法是这样。源数组1是一大叠装订在一起的纸。源数组2也是另一大叠文件。你能快点吗
在上面的类比中,选项1表示array.prototype.concat,而2表示array.prototype.push.apply。让我们用一个类似的JSPERF来测试这一点,只是这个JSPERF的不同之处在于它测试的是稀疏数组而不是实体数组上的方法。你可以在这里找到它。
因此,我认为这个特定用例的未来性能不在array.prototype.push中,而在array.prototype.concat中。
澄清备用阵列当数组的某些成员完全丢失时。例如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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允许您轻松初始化备用阵列。
1 | var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"]; |
数组喜欢
类数组是至少具有
1 | {0:"foo", 1:"bar", length:2} |
1 | document.body.children |
1 | new Uint8Array(3) |
- 这类似于数组,因为尽管它是(n)(类型化)数组,但将其强制为数组会更改构造函数。
1 | (function(){return arguments})() |
使用一种方法来观察会发生什么,这种方法确实会将数组like强制到类似slice的数组中。
1 2 3 4 5 6 7 8 | 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之类的数组的方法来观察会发生什么。
1 2 3 4 5 6 7 8 | 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}() )); |
这是ES6路
1 2 3 4 5 | var newArray = []; let dataArray1 = [1,2,3,4] let dataArray2 = [5,6,7,8] newArray = [...dataArray1, ...dataArray2] console.log(newArray) |
The above method is good to go for most of the cases and the cases it is not please consider
concat , like you have hundred thousands of items in arrays.
1 2 3 4 | let dataArray1 = [1,2,3,4] let dataArray2 = [5,6,7,8] let newArray = dataArray1.concat(dataArray2); console.log(newArray) |
我们有两个数组A和B。这里所做的代码是将数组A的值推入数组B。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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' ] |
使用concat函数代替push()函数。例如,
1 | var a=a.concat(a,new Array('amin')); |
他是一个有效的代码,工作正常:
1 2 3 4 | 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))} |