我有一个 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
,逐个添加项目?
使用 concat 函数,如下所示:
var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);
newArray
的值为 [1, 2, 3, 4]
(arrayA
和 arrayB
保持不变;concat
为结果创建并返回一个新数组)。
在 ECMAScript 6 中,您可以使用 Spread syntax:
让 arr1 = [0, 1, 2];让 arr2 = [3, 4, 5]; arr1.push(...arr2);控制台日志(arr1)
所有主流浏览器(不包括 IE11)都可以使用扩展语法。有关当前兼容性,请参阅此(持续更新)compatibility table。
但是,有关性能的更多评论,请参阅下面 Jack Giffin 的回复。似乎 concat
仍然比扩展运算符更好更快。
newArray.apply(newArray, dataArray1)
。
如果您的数组不是很大(请参阅下面的警告),您可以使用您希望附加值的数组的 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);
... 或通过使用 concat()
(如 push()
)允许多个参数这一事实来模拟原始 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]);
}
};
newArray.push.apply(newArray, dataArray1);
与 Array.prototype.push.applay(newArray,dataArra1);
相同
array.push(...array2);
从 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”并将数组的元素作为剩余参数来构造一个 push 调用。
第一条语句中的 slice
获取第一个数组的副本,因此您无需修改它。
更新如果您使用的 javascript 版本具有可用的 slice,您可以将 push
表达式简化为:
newArray.push(...dataArray2)
𝗥𝗲𝘀𝗲𝗮𝗿𝗰𝗵𝗔𝗻𝗱𝗣𝗼𝘄𝗲𝗿𝗳𝘂𝗹𝗹𝘆𝗦𝗵𝗼𝘄𝗖𝗼𝗻𝗰𝗮𝘁𝗜𝘀𝗧𝗵𝗲(用于原始问题)
对于事实,执行 performance test at jsperf 并检查控制台中的一些内容。对于研究,使用 the website 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 ║MSIENNav║ ╠═══════════════╬══════╬═════════════════╬═══════════════╬═════════╬══════════╣ ║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 trying to 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
的 DOMNodeLists),我建议使用 for 循环,因为它既是性能第二高的方法,也是唯一保留稀疏数组的其他方法。下面,我们将快速了解稀疏数组和类似数组的含义,以消除混淆。
𝗧𝗵𝗲 𝗙𝘂𝘁𝘂𝗿𝗲
起初,有些人可能认为这是侥幸,浏览器供应商最终会设法优化 Array.prototype.push,使其速度足以击败 Array.prototype.concat。错误的! Array.prototype.concat 总是会更快(至少在原则上),因为它是对数据的简单复制粘贴。下面是一个简化的 32 位数组实现可能看起来像的视觉图(请注意实际实现要复杂得多)
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 代表 Array.prototype.concat 而#2 代表 Array.prototype.push.apply。让我们用一个类似的 JSperf 来测试它,不同之处仅在于它测试的是稀疏数组上的方法,而不是实心数组。可以找到它right here。
因此,我认为这个特定用例的性能的未来不在于 Array.prototype.push,而在于 Array.prototype.concat。
𝗖𝗹𝗮𝗿𝗶𝗳𝗶𝗰𝗮𝘁𝗶𝗼𝗻𝘀
𝗦𝗽𝗮𝗿𝗲 𝗔𝗿𝗿𝗮𝘆𝘀
当数组的某些成员完全丢失时。例如:
// 这只是一个例子。在实际代码中, // 不要像这样混合不同的类型。 var mySparseArray = []; mySparseArray[0] = "foo"; mySparseArray[10] = 未定义; mySparseArray[11] = {};我的稀疏数组[12] = 10; mySparseArray[17] = "酒吧"; console.log("长度:", 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)
这类似于数组,因为尽管它是一个(n)(类型化)数组,但将其强制为数组会更改构造函数。
(function(){return arguments})()
观察使用一种将类似数组的方法强制转换为像切片这样的数组的方法会发生什么。
var slice = Array.prototype.slice; // 对于数组:console.log(slice.call(["不是类似数组,而是真正的数组"])); // 对于数组喜欢: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}()));
注意:由于性能原因,在函数参数上调用 slice 是不好的做法。
观察使用不会将类似数组的对象强制转换为 concat 等数组的方法会发生什么。
var 空 = []; // 对于数组:console.log(empty.concat(["不是类似数组,而是真正的数组"])); // 对于类似数组: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 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)
这能解决你的问题吗?
在 JavaScript ES6 中,您可以使用 ... 运算符作为扩展运算符,它将数组转换为值。然后,您可以执行以下操作:
const myArray = [1,2,3,4,5];
const moreData = [6,7,8,9,10];
const newArray = [
...myArray,
...moreData,
];
虽然语法简洁,但我不知道它在内部是如何工作的,以及对大型数组的性能影响是什么。
Array.push.apply
技术慢。
下面的函数对数组的长度没有问题,并且比所有建议的解决方案执行得更好:
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 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
在哪里
for 循环和推送是:
for (var i = 0, l = source.length; i < l; i++) {
target.push(source[i]);
}
推送应用:
target.push.apply(target, source);
传播运算符:
target.push(...source);
最后'设置长度和for循环'是上面的函数
这是 ES6 的方式
var newArray = [];让 dataArray1 = [1,2,3,4] 让 dataArray2 = [5,6,7,8] newArray = [...dataArray1, ...dataArray2] console.log(newArray)
上述方法适用于大多数情况,但不适用于 concat 的情况,例如数组中有数十万个项目。
让 dataArray1 = [1,2,3,4] 让 dataArray2 = [5,6,7,8] 让 newArray = dataArray1.concat(dataArray2);控制台.log(newArray)
如果要修改原始数组,可以传播和推送:
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);
}
有很多关于 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)); // 输出:[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)); // 输出:[1, 2, 3, 4, 5]
表现
我分析了当前的解决方案并提出了 2 个新的(F 和 G 在详细部分中介绍)一个对于中小型阵列来说非常快
今天 2020.11.13 我在 Chrome v86、Safari v13.1.2 和 Firefox v82 上对 MacOs HighSierra 10.13.6 进行测试,以选择解决方案
结果
适用于所有浏览器
基于 while-pop-unshift (F,G) 的解决方案在中小型数组的所有浏览器上(非常)最快。对于具有 50000 个元素的数组,此解决方案在 Chrome 上会减慢速度
数组 500000 中断的解决方案 C、D:“RangeError:超出最大调用堆栈大小
解决方案 (E) 最慢
https://i.stack.imgur.com/sqWsW.png
细节
我执行 2 个测试用例:
当数组有 10 个元素时 - 你可以在这里运行它
当数组有 10k 个元素时 - 你可以在这里运行它
下面的代码片段展示了解决方案 A、B、C、D、E、F(my)、G(my)、H、I 之间的差异
// https://stackoverflow.com/a/4156145/860099 函数 A(a,b) { return a.concat(b); } // https://stackoverflow.com/a/38107399/860099 函数 B(a,b) { return [...a, ...b]; } // https://stackoverflow.com/a/32511679/860099 函数 C(a,b) { return (a.push(...b), a); } // https://stackoverflow.com/a/4156156/860099 函数 D(a,b) { Array.prototype.push.apply(a, b);返回一个; } // https://stackoverflow.com/a/60276098/860099 函数 E(a,b) { return b.reduce((pre, cur) => [...pre, cur], a); } // 我的函数 F(a,b) { while(b.length) a.push(b.shift());返回一个; } // 我的函数 G(a,b) { while(a.length) b.unshift(a.pop());返回 b; } // https://stackoverflow.com/a/44087401/860099 函数 H(a, b) { var len = b.length; var start = a.length; a.length = 开始 + 长度; for (var i = 0; i < len; i++ , start++) { a[start] = b[i]; } 返回一个; } // https://stackoverflow.com/a/51860949/860099 函数 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; } 返回 newArr; } // --------- // 测试 // --------- let a1=[1,2,3];让 a2=[4,5,6]; [A,B,C,D,E,F,G,H,I].forEach(f=> { console.log(`${f.name}: ${f([...a1],[ ...a2])}`) })
这是 chrome 的示例结果
https://i.stack.imgur.com/RlyTk.png
我们有两个数组 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' ]
尝试这个:
var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayB.reduce((pre, cur) => [...pre, ...cur], arrayA);
console.log(newArray)
IE 使用 concat 函数代替 push() 函数。例子,
var a=a.concat(a,new Array('amin'));
Тhis 是一个工作代码,它工作正常:
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))}
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);
}
}
输出:01234 01234
不定期副业成功案例分享
Google Chrome
:快(concat = 获胜者),Opera
:快速(concat = 获胜者),IE
:慢(concat = 获胜者),Firefox
:慢(push.apply = 获胜者,但比慢 10 倍Chrome 的 concat) ...谈到糟糕的 JS 引擎实现。push
不会展平一组值。concat
达到了问题的要求。function(stuff, toAdd) {stuff.push(toAdd);}
此处无法使用concat
。这就是我所说的它们不可互换的意思。以及为什么我认为Array.prototype.push.apply(stuff, toAdd);
是正确答案。