새 배열을 만들지 않고 기존 JavaScript 배열을 다른 배열로 확장하는 방법


970

기존 JavaScript 배열을 다른 배열로 확장하는 방법, 즉 Python의 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) 보다 효율적으로 작동하는 알고리즘을 원합니다 .

참고 : 이것은 배열에 무언가를 추가하는 방법과 중복되지 않습니다 . 여기서 목표는 한 배열의 전체 내용을 다른 배열에 추가하고 "정확한"확장 배열의 모든 요소를 ​​복사하지 않고 수행하는 것입니다.


5
@Toothbrush의 답변에 대한 의견 : a.push(...b). 개념적으로 최고 답변과 비슷하지만 ES6 용으로 업데이트되었습니다.
— tscizzle

답변:


1497

이 .push메소드는 여러 인수를 취할 수 있습니다. spread 연산자 를 사용하여 두 번째 배열의 모든 요소를 ​​인수로 전달할 수 있습니다 .push.

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

브라우저가 ECMAScript 6을 지원하지 않으면 .apply대신 사용할 수 있습니다 .

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

또는 더 명확하다고 생각되면 :

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

배열 b이 너무 길면 이러한 모든 솔루션이 스택 오버플로 오류와 함께 실패 합니다 (브라우저에 따라 약 100,000 개의 요소에서 문제가 시작됨).
그것이 b충분히 짧은 것을 보장 할 수 없다면 , 다른 답변에서 설명 된 표준 루프 기반 기술을 사용해야합니다.


3
나는 이것이 최선의 방법이라고 생각합니다. 다른 어떤 것도 반복이나 apply ()의 또 다른 노력을 포함 할 것입니다
— Peter Bailey

44
"b"(확장 할 배열)가 크면 (내 테스트에 따라 Chrome에서 약 150000 개 항목)이 답변은 실패합니다. for 루프를 사용하거나 "b"에 내장 된 "forEach"기능을 더 잘 사용해야합니다. 내 답변보기 : stackoverflow.com/questions/1374126/…
— jcdude

35
@Deqing : Array의 push메소드는 여러 인수를 취할 수 있으며,이 인수는 배열의 뒤쪽으로 푸시됩니다. 그래서 a.push('x', 'y', 'z')확장하는 유효한 전화입니다 a3 개 요소에 의해. apply는 배열을 가져와 함수에 위치 요소로 명시 적으로 제공된 것처럼 해당 요소를 사용하는 함수의 메서드입니다. 따라서 a.push.apply(a, ['x', 'y', 'z'])배열을 3 요소만큼 확장합니다. 또한 apply컨텍스트를 첫 번째 인수로 사용합니다 ( a에 추가하기 위해 다시 전달 a).
— DzinX

참고 :이 시나리오에서 첫 번째 반환 값은 [1,2,3,4,5]가 아니라 5이고 그 후에 == [1,2,3,4,5]입니다.
— jawo

1
그러나 약간 덜 혼란스럽고 (더 이상 길지 않은) 호출은 다음과 같습니다 [].push.apply(a, b).
— niry

259

업데이트 2018 : 더 나은 대답은 내 새로운 하나입니다 : a.push(...b). 그것은 실제로 질문에 대답하지 않았으므로 더 이상 이것을 찬성하지 마십시오.


단순히 "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];

(또한 복사합니다.)


43
문제는 명확하게 진술했다 : "새로운 배열을 만들지 않고?"
— Wilt

10
@Wilt : 이것은 사실이지만, 대답은 왜 그렇게 말합니다 :) 이것은 Google에서 처음으로 인기가있었습니다. 또한 다른 솔루션은 추악하고 이념적이고 멋진 것을 원했습니다. 요즘에는 arr.push(...arr2)이 특정 질문에 대한 새롭고 더 나은 기술적 인 답변이 있습니다.
— odinho-Velmont

7
나는 당신을 듣고 있지만 "새 배열을 만들지 않고 자바 스크립트 추가 배열"을 검색 한 다음 60 배의 답을 얻은 대답이 실제 질문에 대답하지 않는다는 것을 아는 것은 슬픈 일입니다.) 당신이 당신의 대답을 명확하게하기 때문에.
— Wilt

202

루프 기반 기술을 사용해야합니다. 이 페이지에서 사용을 기반으로하는 다른 답변 .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);

.apply우리가 추가하는 배열이 클 때 DzinX의 답변 (push.apply 사용) 및 기타 기반 메소드가 실패합니다 (테스트 결과 Chrome의 경우 약 150,000 항목, Firefox의 경우 500,000 항목). 이 jsperf 에서이 오류가 발생하는 것을 볼 수 있습니다 .

두 번째 인수로 큰 배열을 사용하여 'Function.prototype.apply'를 호출하면 호출 스택 크기가 초과되어 오류가 발생합니다. MDN은 Function.prototype.apply를 사용하여 호출 스택 크기를 초과 할 위험에 대해 설명합니다. "적용 및 내장 함수"섹션을 참조하십시오.

이 페이지의 다른 답변과 속도를 비교하려면 이 jsperf를 확인하십시오 ( EttererOfCode 덕분에). 루프 기반 구현은을 사용하는 속도와 비슷 Array.push.apply하지만 약간 느립니다 Array.slice.apply.

흥미롭게도, 추가하려는 배열이 희소 인 경우 forEach위 의 기본 방법은 희소성을 활용하고 .apply기본 방법 보다 성능이 우수합니다 . 이것을 직접 테스트하려면 이 jsperf를 확인 하십시오.

그건 그렇고, forEach 구현을 다음과 같이 단축하기 위해 (나처럼) 유혹을받지 마십시오.

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

이것은 쓰레기 결과를 생성하기 때문에! 왜? 때문에 Array.prototype.forEach이 호출하는 함수에 세 가지 인수를 제공 - 다음은 다음과 같습니다 (element_value, element_index, 오기 source_array). forEach"forEach (this.push, this)"를 사용하면이 모든 것들이 반복 될 때마다 첫 번째 배열로 푸시됩니다 !


1
ps other_array가 실제로 배열인지 테스트하려면 여기에 설명 된 옵션 중 하나를 선택하십시오. stackoverflow.com/questions/767486/…
— jcdude

6
좋은 대답입니다. 나는이 문제를 몰랐다. 나는 당신의 대답을 여기에 인용했습니다 : stackoverflow.com/a/4156156/96100
— Tim Down

2
.push.apply실제로 .forEachv8 보다 훨씬 빠르지 만 여전히 인라인 루프가 가장 빠릅니다.
— Benjamin Gruenbaum

1
@ BenjaminGruenbaum-당신은 그것을 보여주는 몇 가지 결과에 대한 링크를 게시 할 수 있습니까? 이 의견 끝에 링크 된 jsperf에서 수집 한 결과에서 볼 수 있듯이 Chrome / Chromium (v25 이후 모든 버전) .forEach보다 사용 속도가 빠릅니다 .push.apply. v8을 단독으로 테스트 할 수는 없었지만 결과를 연결하십시오. jsperf를 참조하십시오 jsperf.com/array-extending-push-vs-concat/5
— jcdude

1
@jcdude 귀하의 jsperf가 유효하지 않습니다. 배열의 모든 요소가 undefined이므로 .forEach건너 뛰 므로 가장 빠릅니다. .splice실제로 가장 빠릅니까?! jsperf.com/array-extending-push-vs-concat/18
— EaterOfCode

152

요즘 가장 우아하다고 생각합니다.

arr1.push(...arr2);

확산 연산자에 대한 MDN 문서 ES2015 (ES6)이 좋은 달콤한 방법을 언급한다 :

더 나은 푸시

예 : push는 배열을 기존 배열의 끝으로 밀어 넣는 데 자주 사용됩니다. ES5에서는 종종 다음과 같이 수행됩니다.

var arr1 = [0, 1, 2];
var arr2 = [3, 4, 5];
// Append all items from arr2 onto arr1
Array.prototype.push.apply(arr1, arr2);

스프레드가있는 ES6에서는 다음과 같이됩니다.

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

참고 수행 arr2거대 호출 스택이 jcdude의 대답에 따라, 오버 플로우 때문에, (000 (100)에 대한 항목에서 보관) 할 수 있습니다.


1
방금 실수로 인한 경고입니다. ES6 버전은 매우 가깝습니다 arr1.push(arr2). 이것은 두 개의 배열이 결합 된 것이 아니라 arr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2)배열의 배열 arr1 == [['a','b','d']]이 되는 것과 같은 문제가 될 수 있습니다 . 만들기 쉬운 실수입니다. 이런 이유로 아래 두 번째 답변을 선호합니다. stackoverflow.com/a/31521404/4808079
— Seph Reed 2016

.concat두 번째 인수가 배열이 아니어야한다는 것이 편리 합니다. 이는 함께 확산 연산자를 조합함으로써 달성 될 수있다 concat예를 들어,arr1.push(...[].concat(arrayOrSingleItem))
— 스티븐 Pribilinskiy을

이것은 대상 배열이 비어 있지 않은 경우 현대적이고 우아한 Javascript입니다.
— timbo

이것으로 충분합니까?
— Roel

@RoelDelosReyes : 예.
— odinho-Velmont

46

apply()JavaScript를 사용하는 이유를 이해하는 데 도움이되는 JavaScript에 대한 몇 마디 :

이 apply()메소드는 주어진 this값을 가진 함수와 배열로 제공된 인수를 호출합니다 .

푸시는 항목 목록 이 배열에 추가 될 것으로 예상합니다 . 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 = [];

1
또는 while (b.length) { a.push(b.shift()); }맞습니까?
— LarsW

37

jQuery를 사용하려면 $ .merge ()가 있습니다.

예:

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

결과 : a = [1, 2, 3, 4, 5]


1
의 구현 (소스 코드에서)을 어떻게 찾을 수 jQuery.merge()있습니까?
— ma11hew28

저에게 10 분이 걸렸습니다-jQuery는 오픈 소스이며 소스는 Github에 게시됩니다. "병합"을 검색하고 core.js 파일에서 병합 기능의 정의 인 것으로 보이는 부분을 추가로 찾아 보았습니다. github.com/jquery/jquery/blob/master/src/core.js#L331
— amn

19

a.push.apply(a, b)위에서 설명한 방법을 좋아하며 원하는 경우 언제든지 다음과 같은 라이브러리 함수를 만들 수 있습니다.

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

이렇게 사용하세요

a = [1,2]
b = [3,4]

a.append(b)

6
"array"인수가 큰 배열 (예 : Chrome의 ~ 150000 개 항목) 인 경우 push.apply 메소드를 사용하면 스택 오버플로가 발생하여 실패 할 수 있습니다. "array.forEach"를 사용해야합니다. 내 답변을 참조하십시오 : stackoverflow.com/a/17368101/1280629
— jcdude

12

다음을 사용하여 수행 할 수 있습니다 splice().

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

그러나 추악함에도 불구하고 push.applyFirefox 3.0에서는 그렇지 않습니다.


9
같은 것을 발견했습니다. splice는 약 10,000 개의 요소 배열이 될 때까지 각 항목을 밀어 넣는 성능 향상을 제공하지 않습니다. jsperf.com/splice-vs-push
— Drew

1
+1이 기능과 성능 비교를 추가해 주셔서 감사합니다.
— jjrv

1
배열 b가 큰 경우 스택 오버플로로 인해 splice.apply 또는 push.apply를 사용하지 못할 수 있습니다. 또한 "for"또는 "forEach"루프를 사용하는 것보다 느립니다. jsperf : jsperf.com/array-extending-push-vs-concat/5 및 내 답변 stackoverflow.com/questions/1374126/…
— jcdude

4

이 솔루션은 저에게 효과적입니다 (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

아래처럼 확장을 위해 폴리 필을 만들 수 있습니다. 배열에 추가됩니다. 다른 메소드를 체인으로 연결할 수 있도록 제자리에 있고 자체적으로 리턴됩니다.

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


0

답변을 결합하는 중 ...

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

9
아니요,이 작업을 수행 할 필요가 없습니다. forEach를 사용하는 for 루프는 push.apply를 사용하는 것보다 빠르며 확장 할 배열의 길이에 관계없이 작동합니다. 수정 된 답변을 살펴보십시오. stackoverflow.com/a/17368101/1280629 어쨌든 모든 브라우저에 150000이 올바른 번호라는 것을 어떻게 알 수 있습니까? 이것은 퍼지입니다.
— jcdude

lol. 내 대답은 인식 할 수 없지만 당시에 발견 된 다른 사람들의 콤보 인 것처럼 보입니다. 즉 요약. 걱정 마세요
— zCoder

0

두 개 이상의 어레이를 병합하는 또 다른 솔루션

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]


-1

대답은 매우 간단합니다.

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

2
MDN 의 문서Array.prototype.concat() 에서 명확하게 언급했듯이 새 Array 를 반환하고 OP가 분명히 요구 한 것처럼 기존 배열에 추가되지 않습니다.
— Wilt

-2

150,000 Array.extend개가 Array.push넘는 레코드 대신 사용하십시오 .

if (!Array.prototype.extend) {
  Array.prototype.extend = function(arr) {
    if (!Array.isArray(arr)) {
      return this;
    }

    for (let record of arr) {
      this.push(record);
    }

    return this;
  };
}

-6

매우 간단하며 스프레드 연산자를 고려하거나 적용되지 않습니다.

b.map(x => a.push(x));

이것에 대한 성능 테스트를 실행 한 후에는 속도가 매우 느리지 만 새 배열을 만들지 않는 것에 대한 질문에 대답합니다. Concat은 훨씬 빠르며 jQuery도 $.merge()그렇습니다.

https://jsperf.com/merge-arrays19b/1


4
반환 값을 사용하지 않는 경우 .forEach보다는 사용해야 .map합니다. 코드의 의도를 더 잘 전달합니다.
— david

@david-각자 자신의. 나는시 낙스를 선호 .map하지만 당신도 할 수 있습니다 b.forEach(function(x) { a.push(x)} ). 사실 jsperf 테스트에 추가했으며 맵보다 머리카락이 빠릅니다. 여전히 매우 느립니다.
— elPastor

3
이것은 선호되는 경우가 아닙니다. 이러한 '기능적'방법은 각각 고유 한 의미를 갖습니다. .map여기서 전화하는 것은 매우 이상하게 보입니다. 전화하는 것과 같습니다 b.filter(x => a.push(x)). 작동하지만 그렇지 않은 경우 발생하는 것을 암시하여 독자를 혼란스럽게합니다.
— david

2
@elPastor 그것은 나와 같은 다른 사람이 앉아서 목에 걸렸을 때 자신의 코드에서 볼 수없는 다른 일이 무엇인지 궁금해하기 때문에 시간이 가치가 있습니다. 대부분의 일반적인 경우 성능이 아니라 의도가 명확합니다. 코드를 읽는 사람들의 시간을 존중하십시오. 하나 일 때 코드가 많을 수 있습니다. 감사.
— Dmytro Y.

1
@elPastor 나는 정확히 무엇을 알고 .map()있으며 이것이 문제입니다. 이 지식은 결과 배열이 필요하다고 생각합니다. 그렇지 않으면 .forEach()콜백 함수의 부작용에 대해 독자가 신중하게 사용하는 것이 좋습니다 . 엄밀히 말하면 솔루션은 추가 자연수 (결과 push)를 생성하는 반면 질문은 "새 배열을 생성하지 않고"라는 질문입니다.
— Dmytro Y.
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.