새 배열을 만들지 않고 기존 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.