将数组项推送到另一个数组中

bba*_*bba 854 javascript arrays

我有一个JavaScript数组dataArray,我想推入一个新的数组newArray.除了我不想newArray[0]成为dataArray.我想将所有项目推入新数组:

var newArray = [];

newArray.pushValues(dataArray1);
newArray.pushValues(dataArray2);
// ...
Run Code Online (Sandbox Code Playgroud)

甚至更好:

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
);
Run Code Online (Sandbox Code Playgroud)

所以现在新数组包含各个数据数组的所有值.是否有一些像pushValues现有的速记,所以我不必迭代每个人dataArray,逐个添加项目?

Wis*_*yEh 1156

使用concat函数,如下所示:

var arrayA = [1, 2];
var arrayB = [3, 4];
var newArray = arrayA.concat(arrayB);
Run Code Online (Sandbox Code Playgroud)

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

  • 我同意高性能执行非常好.**但是**对于**来说,_concat_到数组的目的是什么?所以它应该是**标准**.或者concat还有其他更好的事情吗?它可能很慢,只是因为浏览器的JS引擎实现不好或者你在哪里使用它?有一天它可能会被修复.我会选择代码可维护性而不是hacky速度优化.嗯.... (15认同)
  • _concatenating_ two数组如何_push_ one到另一个?这是两个不同的操作. (9认同)
  • 更好的参考是:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/concat [1]:https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Array/CONCAT (5认同)
  • @WiseGuyEh 想象一下: `function(stuff, toAdd) {stuff.push(toAdd);}` 这里无法使用 `concat` 。这就是我所说的它们不可互换的意思。为什么我相信“Array.prototype.push.apply(stuff, toAdd);”是正确的答案。 (5认同)
  • 此外,我只是对情况进行了基准测试:concat vs. push.apply.`谷歌Chrome`:快速(concat =胜利者),`Opera`:快速(concat =胜利者),`IE`:慢(concat =胜利者),`Firefox`:慢(push.apply =胜利者,但慢10倍)比Chrome的concat)...说到糟糕的JS引擎实现. (3认同)
  • @kaqqao 因为`push` 不会压平一组值。`concat` 达到了问题的要求。 (3认同)
  • @kaqqao 这个问题理想地要求从两个输入数组创建一个新数组,上面的答案实现了这一点。问题的标题可能具有误导性。 (2认同)

Tim*_*own 627

如果您的数组不是很大(请参阅下面的警告),您可以使用push()要附加值的数组的方法.push()可以使用多个参数,以便您可以使用其apply()方法传递要作为函数参数列表推送的值数组.这比使用concat()向阵列添加元素而不是创建新数组更有优势.

但是,似乎对于大型阵列(大约100,000个成员或更多),这个技巧可能会失败.对于这样的数组,使用循环是一种更好的方法.有关详细信息,请参阅/sf/answers/1215767101/.

var newArray = [];
newArray.push.apply(newArray, dataArray1);
newArray.push.apply(newArray, dataArray2);
Run Code Online (Sandbox Code Playgroud)

您可能希望将其概括为函数:

function pushArray(arr, arr2) {
    arr.push.apply(arr, arr2);
}
Run Code Online (Sandbox Code Playgroud)

...或将其添加到Array原型中:

Array.prototype.pushArray = function(arr) {
    this.push.apply(this, arr);
};

var newArray = [];
newArray.pushArray(dataArray1);
newArray.pushArray(dataArray2);
Run Code Online (Sandbox Code Playgroud)

...或者push()通过允许使用多个参数来模拟原始方法concat(),例如push(),允许多个参数:

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

var newArray = [];
newArray.pushArray(dataArray1, dataArray2);
Run Code Online (Sandbox Code Playgroud)

这是最后一个示例的基于循环的版本,适用于大型数组和所有主流浏览器,包括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]);
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 注意:`newArray.push.apply(newArray,dataArray1);`与`Array.prototype.push.applay(newArray,dataArra1)相同;` (8认同)
  • 现在使用扩展运算符,可以像“array.push(...array2);”一样简化 (4认同)

Kar*_*lek 386

我将再添加一个"面向未来"的回复

在ECMAScript 6中,您可以使用spread运算符:

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

console.log(arr1)
Run Code Online (Sandbox Code Playgroud)

Spread运算符尚未包含在所有主流浏览器中.有关当前兼容性,请参阅此(持续更新)兼容性表.

但是,您可以将扩展运算符与Babel.js一起使用.

编辑:

有关性能的更多评论,请参阅下面的Jack Giffin回复.似乎concat仍然比spread运算符更好更快.

  • 如果使用TypeScript,也可以使用spread运算符.如果你的目标是ES5,它将编译为`newArray.apply(newArray,dataArray1)`. (5认同)
  • 注意:如果你需要第三个数组中的结果(因此不修改arr1,因为最初的问题似乎需要),你可以做newArray = [... arr1,... arr2] (5认同)

Bel*_*014 141

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']
Run Code Online (Sandbox Code Playgroud)

或者您可以使用spread operatorES6 的功能:

let fruits = [ 'apple', 'banana'];
const moreFruits = [ 'orange', 'plum' ];

fruits.push(...moreFruits); // ["apple", "banana", "orange", "plum"]
Run Code Online (Sandbox Code Playgroud)

  • 这不是问题的要求。问题是寻求一种每次创建新数组的方法,而不是修改旧数组。 (2认同)

Séb*_*ENT 13

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)
Run Code Online (Sandbox Code Playgroud)

这会解决你的问题吗?


Aru*_*n s 13

我提供了两种简单的方法:

解决方案一:

let dataArray1= [0, 1];
let dataArray1= [2, 3];
dataArray1.push(...dataArray2);
console.log(dataArray1) 
Run Code Online (Sandbox Code Playgroud)

解决方案2:

let dataArray1 = [1, 2];
let dataArray2 = [3, 4];
let newArray = dataArray1.concat(dataArray2);
console.log(newArray)
Run Code Online (Sandbox Code Playgroud)


sha*_*unc 12

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

var newArray = dataArray1.slice();
newArray.push.apply(newArray, dataArray2);
Run Code Online (Sandbox Code Playgroud)

由于"push"采用可变数量的参数,您可以使用函数的apply方法push来推送另一个数组的所有元素.它使用其第一个参数(此处为"newArray")构造一个调用push作为"this",并将数组元素作为其余参数.

slice一个语句中的第一个数组的副本,因此您不需要修改它.

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

newArray.push(...dataArray2)
Run Code Online (Sandbox Code Playgroud)


Kam*_*ski 10

表现

我分析了当前的解决方案并提出了 2 个新的解决方案(F 和 G 在详细信息部分中介绍)。

今天 2023 年 3 月 15 日,我在 Chrome v109、Safari v15.2 和 Firefox v110 上的 MacOs Monterey 12.1(M1,16GB)上对所选解决方案进行测试

结果

对于所有浏览器

  • 基于concat(A) 的解决方案在 Chrome 和 Safari 上对于大数组来说是最快的,在 Firefox 上也相当快。对于小数组来说相当快
  • 基于for(H) 的解决方案在 Firefox 上速度最快,在其他浏览器上速度中等
  • 对于小型阵列,解决方案 C、D 在 chrome 上速度最快
  • 基于reduce(E)的解决方案是最慢的

在此输入图像描述

细节

我执行了 2 个测试用例:

下面的代码片段展示了解决方案 ABCDE、 F(my)、G(my)、 HI之间的差异

// https://stackoverflow.com/a/4156145/860099
function A(a,b) {
  return a.concat(b);
}

// https://stackoverflow.com/a/38107399/860099 
function B(a,b) {
  return [...a, ...b];
}

// https://stackoverflow.com/a/32511679/860099
function C(a,b) {
  return (a.push(...b), a);
}

// https://stackoverflow.com/a/4156156/860099
function D(a,b) {
    Array.prototype.push.apply(a, b);
    return a;
}

// https://stackoverflow.com/a/60276098/860099
function E(a,b) {
    return b.reduce((pre, cur) => [...pre, cur], a);
}

// my
function F(a,b) {
    while(b.length) a.push(b.shift());
    return a;
}

// my
function G(a,b) {
    while(a.length) b.unshift(a.pop());
    return b;
}

// https://stackoverflow.com/a/44087401/860099
function 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/860099
function 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])}`)
})
Run Code Online (Sandbox Code Playgroud)

这是 chrome 的示例结果

在此输入图像描述


Pan*_*eof 9

下面的函数没有数组长度的问题,并且性能优于所有建议的解决方案:

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];
    }
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,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
Run Code Online (Sandbox Code Playgroud)

哪里

for loop和push是:

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

推申请:

target.push.apply(target, source);
Run Code Online (Sandbox Code Playgroud)

传播运营商:

    target.push(...source);
Run Code Online (Sandbox Code Playgroud)

最后'set length和for loop'是上面的函数


Ste*_*uan 9

有很多答案在谈论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]
Run Code Online (Sandbox Code Playgroud)

如果你有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]
Run Code Online (Sandbox Code Playgroud)


ora*_*rad 9

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

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
Run Code Online (Sandbox Code Playgroud)

如果您想确保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);
}
Run Code Online (Sandbox Code Playgroud)


Rya*_* H. 7

使用JavaScript ES6,您可以使用...运算符作为扩展运算符,它实质上将数组转换为值.然后,你可以做这样的事情:

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

const newArray = [
  ...myArray,
  ...moreData,
];
Run Code Online (Sandbox Code Playgroud)

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

  • 如果你看看[如何巴贝尔将其转换(http://babeljs.io/repl/#?babili=false&browsers=&build=&builtIns=false&code_lz=MYewdgzgLgBAhgJwQRhgXhgbQOTgKbYA0M2UA7iNgLoDcAUKJLIggEzpakAWCeBx2AGYgArgmr06LZADoADiIhcAFDLUtWAShpA&debug=false&circleciRepo=&evaluate=false&lineWrap=true&presets=es2015%2Creact%2Cstage -2&prettier = false&targets =&version = 6.26.0),你会发现它不应该比使用`Array.push.apply`技术慢. (2认同)

Jac*_*fin 7

事实是,在jsperf上进行性能测试并检查了控制台中的某些内容。为了进行研究,使用了网站irt.org。下面是所有这些来源的集合,底部是示例函数。

????????????????????????????????????????????????????????? ?????????????????????????????????
?方法?Concat?slice&push.apply?push.apply x2吗?ForLoop?价差?
????????????????????????????????????????????????????????? ?????????????????????????????????
?mOps / Sec?179?104 76?81?28?
????????????????????????????????????????????????????????? ?????????????????????????????????
?稀疏数组吗?仅切成薄片 不行吗 也许2  吗?
?保持稀疏?数组(第一个参数)???
????????????????????????????????????????????????????????? ?????????????????????????????????
?支持?MSIE 4?MSIE 5.5?MSIE 5.5?MSIE 4?Edge 12?
?(来源)?NNav 4?NNav 4.06?NNav 4.06吗?NNav 3吗?MSIE  NNav?
????????????????????????????????????????????????????????? ?????????????????????????????????
类数组的行为否?仅被推动?是!?是!?如果有?
像数组 数组(第二个arg)??迭代器1  ?
????????????????????????????????????????????????????????? ?????????????????????????????????
1如果类数组对象没有Symbol.iterator属性,则尝试
  传播它会引发异常。
2取决于代码。以下示例代码“ YES”保留了稀疏性。
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;
}
Run Code Online (Sandbox Code Playgroud)

如上所示,我认为Concat几乎始终是提高性能和保持备用阵列稀疏性的方法。然后,对于类似数组的对象(例如DOMNodeLists之类的document.body.children),我建议使用for循环,因为它既是性能第二高的方法,又是保留稀疏数组的唯一其他方法。下面,我们将快速浏览稀疏数组和类似数组的含义,以消除混乱。

最初,有些人可能认为这是a幸,浏览器供应商最终会努力优化Array.prototype.push,使其速度足以击败Array.prototype.concat。错误!Array.prototype.concat总是更快(至少在原则上来说如此),因为它是对数据的简单复制-n-粘贴。以下是简化的可视化图表,显示了32位数组实现的外观(请注意,实际实现要复杂得多)

字节?这里的数据
?????????????????
0x00?int nonNumericPropertiesLength = 0x00000000
0x01?同上
0x02?同上
0x03?同上
0x00?整数长度= 0x00000001
0x01?同上
0x02?同上
0x03?同上
0x00?int valueIndex = 0x00000000
0x01?同上
0x02?同上
0x03?同上
0x00?int valueType = JS_PRIMITIVE_NUMBER
0x01?同上
0x02?同上
0x03?同上
0x00?uintptr_t valuePointer = 0x38d9eb60(或内存中的任何位置)
0x01?同上
0x02?同上
0x03?同上

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

另一种思考的方式是这样。源阵列之一是装订在一起的一大堆纸。源阵列2也是另一叠大文件。你会更快吗

  1. 去商店,购买足够的纸张来复制每个源阵列。然后,将每个源阵列纸叠通过复印机,将装订后的两个副本装订在一起。
  2. 去商店,购买足够的纸张以获取第一个源阵列的单个副本。然后,手动将源阵列复制到新纸张上,确保填充任何空白的稀疏斑点。然后,回到商店,为第二个来源阵列购买足够的纸张。然后,检查第二个源数组并复制它,同时确保副本中没有空白。然后,将所有复印的纸张装订在一起。

在上面的类比中,选项#1表示Array.prototype.concat,而#2表示Array.prototype.push.apply。让我们使用类似的JSperf进行测试,不同之处仅在于此代码测试稀疏数组而非实体数组的方法。人们可以在这里找到它。

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

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

???????????????????????????????????????????????????????????????????????????????
? 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 trying
  to spread it will throw an exception.
2 Depends on the code. The following example code "YES" preserves sparseness.

另外,JavaScript可让您轻松初始化备用阵列。

var mySparseArray = ["foo",,,,,,,,,,undefined,{},10,,,,,"bar"];
Run Code Online (Sandbox Code Playgroud)

--

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

{0:“ foo”,1:“ bar”,长度:2}
document.body.children
新的Uint8Array(3)
  • 这类似于数组,因为尽管它是一个(n)(类型化)数组,但将其强制为数组会更改构造函数。
(function(){返回参数})()

观察使用将像数组一样强制转换为像slice这样的数组的方法会发生什么。

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;
}
Run Code Online (Sandbox Code Playgroud)

  • 注意:由于性能,在函数参数上调用slice是一种不好的做法。

观察使用不会强迫像数组这样的数组转换为concat这样的数组的方法会发生什么。

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


Bla*_*mba 6

这是ES6的方式

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

上面的方法适用于大多数情况,不建议考虑的情况concat,例如您有成千上万个数组。

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