首页 文章

如何使用另一个数组扩展现有JavaScript数组,而无需创建新数组

提问于
浏览
748

没有't seem to be a way to extend an existing JavaScript array with another array, i.e. to emulate Python' s extend 方法 .

我想实现以下目标:

>>> a = [1, 2]
[1, 2]
>>> b = [3, 4, 5]
[3, 4, 5]
>>> SOMETHING HERE
>>> a
[1, 2, 3, 4, 5]

我知道有一个 a.concat(b) 方法,但是它创建了一个新数组而不是简单地扩展第一个数组 . 当 a 明显大于 b (即不复制 a 的算法)时,我想要一种有效工作的算法 .

注意:这与如何将某些内容附加到数组不重复? - 这里的目标是将一个数组的全部内容添加到另一个数组中,并“在适当的位置”执行,即不复制扩展数组的所有元素 .

14 回答

  • 1146

    答案非常简单 .

    >>> a = [1, 2]
    [1, 2]
    >>> b = [3, 4, 5]
    [3, 4, 5]
    >>> SOMETHING HERE
    (The following code will combine the two arrays.)
    
    a = a.concat(b);
    
    >>> a
    [1, 2, 3, 4, 5]
    

    Concat的行为与JavaScript字符串连接非常相似 . 它将返回您在调用函数的数组末尾放入concat函数的参数组合 . 关键是您必须将返回的值分配给变量,否则会丢失 . 所以举个例子

    a.concat(b);  <--- This does absolutely nothing since it is just returning the combined arrays, but it doesn't do anything with it.
    
  • 210

    我喜欢上面描述的 a.push.apply(a, b) 方法,如果你想要,你总是可以像这样创建一个库函数:

    Array.prototype.append = function(array)
    {
        this.push.apply(this, array)
    }
    

    并像这样使用它

    a = [1,2]
    b = [3,4]
    
    a.append(b)
    
  • 18

    您可以创建一个polyfill for extend,如下所示 . 它会添加到数组中;就地并返回自己,以便您可以链接其他方法 .

    if (Array.prototype.extend === undefined) {
      Array.prototype.extend = function(other) {
        this.push.apply(this, arguments.length > 1 ? arguments : other);
        return this;
      };
    }
    
    function print() {
      document.body.innerHTML += [].map.call(arguments, function(item) {
        return typeof item === 'object' ? JSON.stringify(item) : item;
      }).join(' ') + '\n';
    }
    document.body.innerHTML = '';
    
    var a = [1, 2, 3];
    var b = [4, 5, 6];
    
    print('Concat');
    print('(1)', a.concat(b));
    print('(2)', a.concat(b));
    print('(3)', a.concat(4, 5, 6));
    
    print('\nExtend');
    print('(1)', a.extend(b));
    print('(2)', a.extend(b));
    print('(3)', a.extend(4, 5, 6));
    
    body {
      font-family: monospace;
      white-space: pre;
    }
    
  • 2

    我可以看到非常好的答案,这一切都取决于您的阵列大小,您的要求和目标 . 它可以以不同的方式完成 .

    我建议使用JavaScript for循环:

    var a = [1, 2];
    var b = [3, 4, 5];
    
    for (i = 0; i < b.length; i++) {
        a.push(b[i]);
    }
    

    console.log(a) 输出将是

    Array [ 1, 2, 3, 4, 5 ]
    

    然后你可以自己做功能:

    function extendArray(a, b){
        for (i = 0; i < b.length; i++) {
            a.push(b[i]);
        }
        return a;
    }
    

    console.log(extendArray(a, b)); 输出将是

    Array [ 1, 2, 3, 4, 5 ]
    
  • 131

    这些天我觉得最优雅的是:

    arr1.push(...arr2);
    

    MDN article on the spread operator在ES2015(ES6)中提到了这种不错的含糖方式:

    更好的推送示例:push通常用于将数组推送到现有数组的末尾 . 在ES5中,通常这样做:var arr1 = [0,1,2];
    var arr2 = [3,4,5];
    //将arr2中的所有项目附加到arr1
    Array.prototype.push.apply(arr1,arr2);
    在带有传播的ES6中,变为:var arr1 = [0,1,2];
    var arr2 = [3,4,5];
    arr1.push(... ARR2);

    请注意 arr2 可以't be huge (keep it under about 100 000 items), because the call stack overflows, as per jcdude'回答 .

  • 0

    您应该使用基于循环的技术 . 此页面上基于使用 .apply 的其他答案可能会因大型阵列而失败 .

    一个相当简洁的基于循环的实现是:

    Array.prototype.extend = function (other_array) {
        /* You should include a test to check whether other_array really is an array */
        other_array.forEach(function(v) {this.push(v)}, this);
    }
    

    然后,您可以执行以下操作:

    var a = [1,2,3];
    var b = [5,4,3];
    a.extend(b);
    

    当我们追加的数组很大时,DzinX's answer(使用push.apply)和其他基于 .apply 的方法失败(测试显示,对于我来说,大约在Chrome中大于150,000个条目,在Firefox中大于500,000个条目) . 您可以在this jsperf中看到此错误 .

    发生错误是因为在使用大数组作为第二个参数调用'Function.prototype.apply'时超出了调用堆栈大小 . (MDN记录了使用Function.prototype.apply超出调用堆栈大小的危险 - 请参阅 Headers 为"apply and built-in functions"的部分 . )

    要与本页面上的其他答案进行速度比较,请查看this jsperf(感谢EaterOfCode) . 基于循环的实现与使用 Array.push.apply 的速度相似,但往往比 Array.slice.apply 慢一点 .

    有趣的是,如果你追加的数组是稀疏的,那么上面基于_350333的方法可以利用稀疏性并且优于基于 .apply 的方法;如果你想自己测试一下,请查看this jsperf .

    顺便说一句,不要被诱惑(像我一样!)进一步缩短forEach实现:

    Array.prototype.extend = function (array) {
        array.forEach(this.push, this);
    }
    

    因为这会产生垃圾结果!为什么?因为 Array.prototype.forEach 为它调用的函数提供了三个参数 - 这些参数是:(element_value,element_index,source_array) . 如果您使用"forEach(this.push, this)",所有这些都将被推送到您的第一个数组,每次迭代 forEach

  • 38

    如果你想使用jQuery,有$.merge()

    例:

    a = [1, 2];
    b = [3, 4, 5];
    $.merge(a,b);
    

    结果:a = [1, 2, 3, 4, 5]

  • 0

    可以使用 splice() 来完成:

    b.unshift(b.length)
    b.unshift(a.length)
    Array.prototype.splice.apply(a,b) 
    b.shift() // Restore b
    b.shift() //
    

    但是尽管比较丑陋,它并不比 push.apply 快,至少在Firefox 3.0中没有 .

  • 45

    合并两个以上数组的另一种解决方案

    var a = [1, 2],
        b = [3, 4, 5],
        c = [6, 7];
    
    // Merge the contents of multiple arrays together into the first array
    var mergeArrays = function() {
     var i, len = arguments.length;
     if (len > 1) {
      for (i = 1; i < len; i++) {
        arguments[0].push.apply(arguments[0], arguments[i]);
      }
     }
    };
    

    然后打电话和打印为:

    mergeArrays(a, b, c);
    console.log(a)
    

    输出将是: Array [1, 2, 3, 4, 5, 6, 7]

  • 181

    更新2018年:更好的答案是我的新答案:a.push(... b) . 不要再投票了,因为它从来没有真正回答过这个问题,但它是2015年黑客入侵谷歌首次亮相:)


    对于那些只是搜索"JavaScript array extend"并到达这里的人来说,你可以很好地使用 Array.concat .

    var a = [1, 2, 3];
    a = a.concat([5, 4, 3]);
    

    Concat将返回一个新数组的副本,因为线程启动程序不想要 . 但你可能不在乎(当然对于大多数用途来说这样会很好) .


    以扩展运算符的形式还有一些不错的ECMAScript 6糖:

    const a = [1, 2, 3];
    const b = [...a, 5, 4, 3];
    

    (它也复制 . )

  • 13

    首先在JavaScript中使用 apply() 来帮助理解我们使用它的原因:

    apply()方法调用具有给定此值的函数,并将参数作为数组提供 .

    Push期望要添加的项目列表数组 . 但是, apply() 方法将函数调用的预期参数作为数组 . 这允许我们使用内置 push() 方法轻松地将一个数组的元素 push 转换为另一个数组 .

    想象一下你有这些数组:

    var a = [1, 2, 3, 4];
    var b = [5, 6, 7];
    

    并简单地这样做:

    Array.prototype.push.apply(a, b);
    

    结果将是:

    a = [1, 2, 3, 4, 5, 6, 7];
    

    使用扩展运算符(“ ... ”)可以在ES6中完成同样的事情,如下所示:

    a.push(...b); //a = [1, 2, 3, 4, 5, 6, 7];
    

    目前所有浏览器都更短,更好但不完全支持 .

    此外,如果要将所有内容从数组 b 移动到 a ,在此过程中清空 b ,您可以执行以下操作:

    while(b.length) {
      a.push(b.shift());
    }
    

    结果如下:

    a = [1, 2, 3, 4, 5, 6, 7];
    b = [];
    
  • 3

    结合答案......

    Array.prototype.extend = function(array) {
        if (array.length < 150000) {
            this.push.apply(this, array)
        } else {
            for (var i = 0, len = array.length; i < len; ++i) {
                this.push(array[i]);
            };
        }  
    }
    
  • 1

    这个解决方案对我有用(使用ECMAScript 6的扩展运算符):

    let array = ['my', 'solution', 'works'];
    let newArray = [];
    let newArray2 = [];
    newArray.push(...array); // Adding to same array
    newArray2.push([...array]); // Adding as child/leaf/sub-array
    console.log(newArray);
    console.log(newArray2);
    
  • -1

    .push方法可以使用多个参数,因此通过使用.apply将第二个数组的所有元素作为参数传递给 .push ,您可以获得所需的结果:

    >>> a.push.apply(a, b)
    

    或许,如果你认为它更清楚:

    >>> Array.prototype.push.apply(a,b)
    

    如果您的浏览器支持ECMAScript 6,您可以使用spread operator,它更紧凑和优雅:

    >>> a.push(...b)
    

    请注意,如果数组 b 太长,所有这些解决方案都将失败并出现堆栈溢出错误(麻烦从大约100,000个元素开始,具体取决于浏览器) . 如果你不能保证 b 足够短,你应该使用另一个答案中描述的基于循环的标准技术 .

相关问题