首页 文章

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

提问于
浏览
712

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

15 回答

  • 266

    有很多答案在谈论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]
    
  • 603

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

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

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

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

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

    newArray.push(...dataArray2)
    
  • -2

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

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

    最简单:

    var newArray = dataArray1.slice(0);
    
  • 132

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

    但是,似乎对于大型阵列(大约100,000个成员或更多), this trick can fail . 对于这样的数组,使用循环是一种更好的方法 . 有关详细信息,请参阅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]);
        }
    };
    
  • -3

    使用concat函数,如下所示:

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

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

  • 11

    Here's the ES6 way

    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)
    
  • 0

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

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

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

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

    这会解决你的问题吗?

  • 12

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

    在ECMAScript 6中,您可以使用spread operator

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

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

    但是,您可以使用带有Babel.js的扩展运算符 .

    编辑:

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

  • 3

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

    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和for loop'是上面的函数

  • 959

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

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

    研究和结果

    对于事实,执行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循环,因为它是第二个最高效的,也是唯一一个保留稀疏数组的方法 . 下面,我们将快速浏览稀疏数组和数组类似的意思,以消除混淆 .

    未来

    起初,有些人可能认为这是一个侥幸,浏览器供应商最终会优化Array.prototype.push,以便足够快地击败Array.prototype.concat . 错误!Array.prototype.concat总是更快(原则上至少),因为它是对数据的简单复制粘贴 . 下面是一个简化的persuado-visual图,显示了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 .

    澄清

    备用阵列

    当阵列的某些成员丢失时 . 例如:

    // 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}() ));
    
    • 注意:由于性能,切片函数参数是不好的做法 .

    观察使用 not 将数组喜欢强制转换为像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}() ));
    
  • 9

    我们有两个数组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' ]
    
  • 7

    找到一个优雅的方式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"]
    

相关问题