Javascript-다른 배열 안에 배열 삽입


87

다른 배열 안에 배열을 삽입하는 더 효율적인 방법은 무엇입니까?

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

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

splice를 사용하여 a2를 반복하는 것은 a2 배열이 큰 경우 성능 관점에서 약간 끔찍하게 보입니다.

감사.



2
하나의 항목이 아니라 배열이므로 splice가 작동하지 않습니다.
ic3 aug

답변:


179

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에서 테스트 됨) . 물론 이것은 대부분의 경우 가독성이 속도보다 중요하다는 표준 경고와 함께 제공됩니다.
nickf

3
@nick : 크롬에서 배열이 130000 개 이상의 항목 apply을 보유 하면 stackoverflow 예외가 발생합니다. jsfiddle.net/DcHCY jsPerf 에서도 발생합니다. 저는 FF와 IE에서 임계 값이 약 500k라고 믿습니다
Nope

splice를 직접 호출하고 인수를 전달하는 대신 apply를 사용하는 이유는 무엇입니까?
Peter P.

@ FrançoisWahl, 이것은 사람들이 답변에서 종종 무시하는 심각한 문제입니다. I의 한 귀하의 예를 가져다가 1M 요소의 북쪽으로 작동했다 : stackoverflow.com/a/41466395/1038326
가브리엘 Kohen

방금 시도해 보았습니다 (2017 년 6 월). 작은 어레이 크기와 큰 어레이 크기 (Chrome, FF 및 Edge에서) 모두에 대한 가장 빠른 방법은 target.slice(0, insertIndex).concat(insertArr, target.slice(insertIndex)); jsperf.com/inserting-an-array-within-an-array
Alex 디마


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시작 인덱스이고 변경되지는 않지만 2deleteCount 요소 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)양의 정수 인수를 갖는에 대한 호출 은 자연수로 계산하여 두 번째 요소 이후의 모든 요소를 ​​반환합니다 (에서와 같이 5 개의 요소 [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 );

7이 아닌 6 개의 항목이있는
a1-

작동합니다! 고마워요, 며칠 기다려 보겠습니다.하지만 이것이 최선의 답변입니다 ... 이상하게 여기에 js 함수가 없습니다.
ic3

내가 더 좋아이 한
김치 남자

요약하면 JS 엔진은 표현식이 완료되기 전에 4 개의 배열을 반환해야합니다. 나는 솔루션의 논리를 좋아하지만 공간 고려 측면에서 최적이 아닐 수 있습니다. 그러나 나는 그것이 멋지고 매우 크로스 브라우저 호환이라는 데 동의합니다. 컬렉션에서 작동하는 스프레드 연산자를 사용하는 데는 비용이 들지만 장기적으로는 4 개의 배열을 반환하는 것보다 더 많은 비용이 발생합니다.
Anthony Rutledge 2019

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대신 사용하여 ECMAScript 3 및 5에서 작동 합니다.letconst

** 7) 제시된 다른 많은 솔루션과 달리 머리와 꼬리가 몸을 때릴 수 있도록합니다. 경계 앞이나 뒤에 배열을 추가하는 경우 적어도 .concat()!!!! 를 사용해야합니다.

참고 : Spread opearator를 ...사용하면이 모든 작업을 훨씬 쉽게 수행 할 수 있습니다.


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.
nickf

나는 그것이 a2를 수정하고 있다는 것을 알고 있었다. OP는 그것이 문제인지 아닌지를 결정할 수 있습니다. 메서드를 얻기 위해 프로토 타입으로 이동하는 데 문제가 있습니까? 메서드를 원했고 메서드에 개체가 추가되고 있기 때문에 나에게 더 적절 해 보였습니다 apply().
jfriend00 2011-08-11

아니, 똑같은 기능이지만 하나는 더 짧습니다. Array.prototype.splicevs a1.splice:)
nickf

범용 배열 삽입 기능이 추가되었습니다.
jfriend00 2011-08-11

.concat새 배열을 반환하고 .NET과 같은 기존 배열을 수정하지 않습니다 splice. 되어야합니다args = args.concat(arrayToInsert);
nickf 2011-08-11

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

이 경우 각 반복마다 insert_index를 증가시키는 대신 new_values ​​배열을 .reverse ()하는 것이 더 간단 할 수 있습니다. 물론 start_index가 0이거나 start_index-1 일 때이 솔루션의 효율성은 명시 적 루프없이 .concat ()을 사용하거나를 사용하여 unshift () 또는 push ()`를 사용하는 것과 비교할 때 좋지 않습니다 .apply().
Anthony Rutledge

0

새 배열을 만들지 않고 다른 배열을 배열에 삽입하려는 경우 가장 쉬운 방법은 push또는 unshift함께 사용하는 것 입니다.apply

예 :

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]);
       }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.