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

时间:2010-11-11 15:33:19

标签: javascript arrays

我有一个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,逐个添加项目?

16 个答案:

答案 0 :(得分:1060)

使用concat功能,如下所示:

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

newArray的值为[1, 2, 3, 4]arrayAarrayB保持不变; concat创建并返回结果的新数组。< / p>

答案 1 :(得分:614)

如果您的数组不是很大(请参阅下面的警告),您可以使用要附加值的数组的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&lt; = 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]);
    }
};

答案 2 :(得分:323)

我将再增加一个&#34;面向未来&#34;回复

在ECMAScript 6中,您可以使用precedence

{{1}}

Spread运算符尚未包含在所有主流浏览器中。有关当前兼容性,请参阅此(不断更新)spread operator

但是,您可以将扩展运算符与compatibility table一起使用。

编辑:

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

答案 3 :(得分:137)

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

答案 4 :(得分:12)

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)

这能解决您的问题吗?

答案 5 :(得分:9)

有许多答案在讨论Array.prototype.push.apply。这是一个明显的例子:

&#13;
&#13;
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]
&#13;
&#13;
&#13;

如果你有ES6语法:

&#13;
&#13;
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]
&#13;
&#13;
&#13;

答案 6 :(得分: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的结果

        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 loop和push是:

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

推送申请:

target.push.apply(target, source);

传播运营商:

    target.push(...source);

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

答案 7 :(得分:7)

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

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

const newArray = [
  ...myArray,
  ...moreData,
];

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

答案 8 :(得分: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)

答案 9 :(得分:4)

研究和结果

对于事实,执行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  ║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.
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循环,因为它既是性能第二高的方法,又是保留稀疏数组的唯一其他方法。下面,我们将快速浏览稀疏数组和类似数组的含义,以消除混乱。

未来

起初,有些人可能认为这是a幸,浏览器供应商最终将绕过优化Array.prototype.push的速度,以使其快于Array.prototype.concat。错误! Array.prototype.concat总是更快(至少在原则上来说如此),因为它是对数据的简单复制-n-粘贴。下面是简化的可视化图表,显示了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每次都必须另外分配更多的内存,并且(对于某些浏览器实现)甚至可能重新计算一些位置查找数据以确保稀疏。

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

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

在上述类比中,选项#1表示Array.prototype.concat,而#2表示Array.prototype.push.apply。让我们用类似的JSperf来测试这一点,不同之处仅在于此测试是在稀疏数组而非实体数组上测试方法。可以找到它right here

因此,我想证明这种特定用例的性能未来不在于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"];

Array-likes

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

{0: "foo", 1: "bar", length:2}
document.body.children
new Uint8Array(3)
  • 这类似于数组,因为尽管它是一个(n)(类型化)数组,但将其强制为数组会更改构造函数。
(function(){return arguments})()

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

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

  • 注意:由于性能原因,对函数参数进行切片是不好的做法。

观察使用将类似数组的值强制转换为像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}() ));

答案 10 :(得分:0)

我们有两个数组a和b。这里的代码是数组,一个值被推入数组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' ]

答案 11 :(得分:0)

如果要修改原始数组,可以展开并推动:

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

答案 12 :(得分:0)

尝试一下:

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

答案 13 :(得分:0)

非常快

我分析了当前的解决方案,并提出了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)最快

enter image description here

详细信息

我执行2个测试用例:

  • 当数组有10个元素时-您可以HERE运行它
  • 当数组具有1万个元素时-您可以HERE运行它

以下代码段显示了解决方案之间的差异 ABCDE, F(我), G(我) 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])}`)
})

这是铬的示例结果

enter image description here

答案 14 :(得分:-2)

而不是push()函数使用IE的concat函数。例如,

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

答案 15 :(得分:-3)

这是一个有效的代码,工作正常:

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