Javascript-在另一个数组内插入一个数组


87

在另一个数组中插入数组的更有效方法是什么。

a1 = [1,2,3,4,5];
a2 = [21,22];

newArray - a1.insertAt(2,a2) -> [1,2, 21,22, 3,4,5];

如果a2数组很大,从性能的角度来看,使用拼接来迭代a2看起来会有些糟。

谢谢。



2
它不是一个项目而是一个数组,所以拼接无法正常工作
ic3 2011年

Answers:


177

您可以splice结合使用一些apply技巧:

a1 = [1,2,3,4,5];
a2 = [21,22];

a1.splice.apply(a1, [2, 0].concat(a2));

console.log(a1); // [1, 2, 21, 22, 3, 4, 5];

在ES2015 +中,您可以改用散布运算符使它更好

a1.splice(2, 0, ...a2);

10
@icCube:我只是运行了一个基准,将该方法与patrick的答案进行了比较。如示例所示,它以a1和a2开头,并将a2变量注入a1 10,000次(因此最后,您有一个包含20,005个元素的数组)。该方法耗时:81ms,slice + concat方法耗时156ms (在Chrome 13上测试)。当然,这带有标准警告,在大多数情况下,易读性比速度更重要。
尼克

3
@nick:在chrome中,如果数组容纳超过130000个项目,apply将引发stackoverflow异常。jsfiddle.net/DcHCY也发生在jsPerf中,我相信FF和IE的阈值大约为500k
Nope

为什么使用apply而不是直接调用splice并传递参数?
Peter P.

@FrançoisWahl,这是一个严重的问题,人们通常在回答时会忽略。我以您的示例为例,并使其与1M元素以北的组件一起使用:stackoverflow.com/a/41466395/1038326
Gabriel Kohen

我刚刚尝试过(2017年6月),无论是小型阵列还是大型阵列(在Chrome,FF和Edge上),最快的方法似乎都是target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex Dima


14

起初有错。应该使用concat()代替。

var a1 = [1,2,3,4,5],
    a2 = [21,22],
    startIndex = 0,
    insertionIndex = 2,
    result;    

result = a1.slice(startIndex, insertionIndex).concat(a2).concat(a1.slice(insertionIndex));

示例: http //jsfiddle.net/f3cae/1/

该表达式使用slice(0, 2)[docs]返回的前两个元素a1(其中0,开始索引,2元素deleteCount,尽管a1未更改)。

中间结果[1,2]

然后,它使用concat(a2)[docs]附加a2到的末尾[1,2]

中间结果[1,2,21,22]

接下来,在该表达式a1.slice(2)尾部.concat()的尾部被调用,总计为[1,2,21,22].concat(a1.slice(2))

调用slice(2)具有正整数参数的,将返回第二个元素之后的所有元素,并以自然数计数(例如,有五个元素,因此[3,4,5]将从返回a1)。另一种说法是,奇异整数索引参数告诉a1.slice()数组从哪个位置开始返回元素(索引2是第三个元素)。

中间结果[1,2,21,22].concat([3,4,5])

最后,第二个.concat()添加[3,4,5]到的末尾[1,2,21,22]

结果[1,2,21,22,3,4,5]

进行更改可能很吸引人Array.prototype,但是您可以使用原型继承简单地扩展Array对象,并将所述新对象注入您的项目中。

然而,对于那些生活在边缘的人...

示例: http //jsfiddle.net/f3cae/2/

Array.prototype.injectArray = function( idx, arr ) {
    return this.slice( 0, idx ).concat( arr ).concat( this.slice( idx ) );
};

var a1 = [1,2,3,4,5];
var a2 = [21,22];

var result = a1.injectArray( 2, a2 );

我得到6个项不是1的
a1-

工程!非常感谢,让我们等待几天,但这是最好的答案...奇怪的是,他们没有js函数。
2011年

我更喜欢这个
Kimchi Man

总之,在完成表达式之前,JS引擎必须返回四个数组。我喜欢该解决方案的逻辑,但是从空间考虑来看,它可能不是最佳选择。但是,我同意它很漂亮并且非常兼容跨浏览器。使用散布运算符在集合上工作时会付出一定的代价,但是从长远来看,我可能会认为这超过返回四个数组。
Anthony Rutledge

3

传播操作符允许表达式来在多个参数(对于函数调用)或多个元素(数组文本)预计的地方扩大。

a2 = [21,22];
a1 = [1,2,...a2,3,4,5];//...a2 is use of spread operator
console.log(a1);


3

这里有一些真正有创意的答案。对于刚从数组开始的人来说,这是一个简单的解决方案。如果需要,可以使其一直运行到兼容ECMAScript 3的浏览器。

开始之前请先了解一些有关拼接的知识。

Mozilla开发人员网络:Array.prototype.splice()

首先,了解的两种重要形式.splice()

let a1 = [1,2,3,4],
    a2 = [1,2];

方法1)从所需的索引开始,删除x(deleteCount)元素。

let startIndex = 0, 
    deleteCount = 2;

a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

方法2)删除所需的起始索引后到数组的末尾的元素。

a1.splice(2); // returns [3,4], a1 would be [1,2]

使用.splice(),目标可能是a1使用上述两种形式之一将其拆分为头尾阵列。

使用方法1,返回值将成为头和a1尾。

let head = a1.splice(startIndex, deleteCount); // returns [1,2], a1 would be [3,4]

现在,一举将头,身体(a2)和尾巴连接起来

[].concat(head, a2, a1);

因此,此解决方案比到目前为止提供的任何其他解决方案都更像现实世界。这不是您乐高积木会做的吗?;-)这是一个使用方法#2完成的函数。

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    let tail = target.splice(startIndex); // target is now [1,2] and the head
    return [].concat(target, body, tail);
}

let newArray = insertArray([1, 2, 3, 4], ["a", "b"], 2); // [1, 2, "a", "b", 3, 4]

更短:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*/
function insertArray(target, body, startIndex)
{
    return [].concat(target, body, target.splice(startIndex));
}

更安全:

/**
*@param target Array The array to be split up into a head and tail.
*@param body Array The array to be inserted between the head and tail.
*@param startIndex Integer Where to split the target array.
*@throws Error The value for startIndex must fall between the first and last index, exclusive.
*/
function insertArray(target, body, startIndex)
{
    const ARRAY_START = 0,
          ARRAY_END = target.length - 1,
          ARRAY_NEG_END = -1,
          START_INDEX_MAGNITUDE = Math.abs(startIndex);

    if (startIndex === ARRAY_START) {
        throw new Error("The value for startIndex cannot be zero (0).");
    }

    if (startIndex === ARRAY_END || startIndex === ARRAY_NEG_END) {
        throw new Error("The startIndex cannot be equal to the last index in target, or -1.");
    }

    if (START_INDEX_MAGNITUDE >= ARRAY_END) {
        throw new Error("The absolute value of startIndex must be less than the last index.");
    }

    return [].concat(target, body, target.splice(startIndex));
}

该解决方案的优点包括:

1)一个简单的前提主导解决方案-填充一个空数组。

2)头,身体和尾部的命名法很自然。

3)没有双重呼叫.slice()。完全没有切片。

4)没有.apply()。完全没有必要。

5)避免方法链接。

6)只需使用var代替let或即可在ECMAScript 3和5 中工作const

** 7)确保头和尾巴可以贴在身上,这与其他许多解决方案不同。如果要在边界之前或之后添加数组,则至少应使用.concat()!!!!

注意:使用扩散式起皱...器使所有这些操作变得更加容易。


2

我想找到一种splice()无需反复进行的方法:http : //jsfiddle.net/jfriend00/W9n27/

a1 = [1,2,3,4,5];
a2 = [21,22];

a2.unshift(2, 0);          // put first two params to splice onto front of array
a1.splice.apply(a1, a2);   // pass array as arguments parameter to splice
console.log(a1);           // [1, 2, 21, 22, 3, 4, 5];

通用功能形式:

function arrayInsertAt(destArray, pos, arrayToInsert) {
    var args = [];
    args.push(pos);                           // where to insert
    args.push(0);                             // nothing to remove
    args = args.concat(arrayToInsert);        // add on array to insert
    destArray.splice.apply(destArray, args);  // splice it in
}

a2也会修改数组,这可能是非常不可取的。另外,Array.prototype当您在a1或上具有切片功能时,无需转到a2
尼克

我知道它正在修改a2。OP可以决定是否存在问题。去原型获取方法有什么问题吗?这对我来说似乎更合适,因为我只想要该方法,并且在该apply()方法中添加了对象。
jfriend00

好吧,不,它是完全相同的功能,但是功能更短:Array.prototype.splicevs a1.splice:)
nickf

添加了通用数组插入功能。
jfriend00 2011年

.concat返回一个新数组,它不会修改现有数组,例如splice。应该是args = args.concat(arrayToInsert);
nickf

0
var a1 = [1,2,3,4,5];
var a2 = [21,22];

function injectAt(d, a1, a2) {
    for(var i=a1.length-1; i>=d; i--) {
        a1[i + a2.length] = a1[i];
    }
    for(var i=0; i<a2.length; i++) {
        a1[i+d] = a2[i];
    }
}

injectAt(2, a1, a2);

alert(a1);

0

这是我的版本,没有任何特殊技巧:

function insert_array(original_array, new_values, insert_index) {
    for (var i=0; i<new_values.length; i++) {
        original_array.splice((insert_index + i), 0, new_values[i]);
    }
    return original_array;
}

在这种情况下,仅.reverse()new_values数组可能会更简单,而不是为每次迭代增加insert_index。当然,当start_index为零或start_index-1时,此解决方案的效率较差,与仅使用.concat()而不使用任何显式循环或unshift()或push()`相比.apply()
安东尼·鲁特里奇

0

如果你想插入另一个数组的数组,而无需创建一个新的,最简单的方法是要么使用pushunshiftapply

例如:

a1 = [1,2,3,4,5];
a2 = [21,22];

// Insert a1 at beginning of a2
a2.unshift.apply(a2,a1);
// Insert a1 at end of a2
a2.push.apply(a2,a1);

这工作,因为两者pushunshift带有可变数量的参数。另外,您可以轻松地选择从哪个末端连接阵列!


只是这样做,a1.concat(a2)否则a2.concat(a1)我已经设计出比您建议的要容易的东西。但是,问题是在数组边界之间插入数组,而不是仅在数组的开头或结尾添加数组。;-)
Anthony Rutledge

0

就像在另一个线程中提到的那样,上面的答案不适用于非常大的数组(200K个元素)。在此处查看涉及接合和手动推动的替代答案:https : //stackoverflow.com/a/41465578/1038326

Array.prototype.spliceArray = function(index, insertedArray) {
    var postArray = this.splice(index);
    inPlacePush(this, insertedArray);
    inPlacePush(this, postArray);

    function inPlacePush(targetArray, pushedArray) {
  // Not using forEach for browser compatability
        var pushedArrayLength = pushedArray.length;
        for (var index = 0; index < pushedArrayLength; index++) {
           targetArray.push(pushedArray[index]);
       }
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.