Cara memperluas array JavaScript yang ada dengan array lain, tanpa membuat array baru


974

Tampaknya tidak ada cara untuk memperluas array JavaScript yang ada dengan array lain, yaitu meniru extendmetode Python .

Saya ingin mencapai yang berikut:

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

Saya tahu ada a.concat(b)metode, tetapi menciptakan array baru alih-alih hanya memperpanjang yang pertama. Saya ingin algoritma yang bekerja secara efisien bila ajauh lebih besar daripada b(yaitu yang tidak menyalin a).

Catatan: Ini bukan duplikat dari Bagaimana cara menambahkan sesuatu ke array? - tujuannya di sini adalah untuk menambahkan seluruh konten dari satu array ke array lainnya, dan untuk melakukannya "di tempat", yaitu tanpa menyalin semua elemen dari array yang diperluas.


5
Dari @ komentar sikat gigi pada jawaban: a.push(...b). Konsepnya mirip dengan jawaban teratas, tetapi diperbarui untuk ES6.
tscizzle

Jawaban:


1501

The .pushMetode dapat mengambil beberapa argumen. Anda bisa menggunakan operator spread untuk meneruskan semua elemen array kedua sebagai argumen untuk .push:

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

Jika browser Anda tidak mendukung ECMAScript 6, Anda dapat menggunakannya .apply:

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

Atau mungkin, jika Anda pikir itu lebih jelas:

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

Harap dicatat bahwa semua solusi ini akan gagal dengan kesalahan stack overflow jika array bterlalu panjang (masalah dimulai pada sekitar 100.000 elemen, tergantung pada browser).
Jika Anda tidak dapat menjamin bahwa bitu cukup singkat, Anda harus menggunakan teknik berbasis loop standar yang dijelaskan dalam jawaban lainnya.


3
Saya pikir ini adalah taruhan terbaik Anda. Hal lain akan melibatkan iterasi atau aktivitas lain dari aplikasi ()
Peter Bailey

44
Jawaban ini akan gagal jika "b" (array untuk memperpanjang) besar (> 150000 entri kira-kira di Chrome sesuai dengan pengujian saya). Anda harus menggunakan for for loop, atau bahkan lebih baik menggunakan fungsi "forEach" inbuilt pada "b". Lihat jawaban saya: stackoverflow.com/questions/1374126/…
jcdude

35
@Deqing: pushMetode Array dapat mengambil sejumlah argumen, yang kemudian didorong ke belakang array. Begitu a.push('x', 'y', 'z')juga panggilan yang valid yang akan diperpanjang aoleh 3 elemen. applyadalah metode dari setiap fungsi yang mengambil array dan menggunakan elemen-elemennya seolah-olah mereka semua diberikan secara eksplisit sebagai elemen posisi ke fungsi. Begitu a.push.apply(a, ['x', 'y', 'z'])juga akan memperluas array dengan 3 elemen. Selain itu, applygunakan konteks sebagai argumen pertama (kami sampaikan alagi di sana untuk ditambahkan ke a).
DzinX

Catatan: dalam skenario ini, nilai pengembalian pertama bukan [1,2,3,4,5] tetapi 5 sedangkan a == [1,2,3,4,5] sesudahnya.
jawo

1
Namun lain sedikit kurang membingungkan (dan tidak banyak lagi) doa akan menjadi: [].push.apply(a, b).
niry

259

Update 2018 : Sebuah jawaban yang lebih baik adalah salah satu yang lebih baru saya : a.push(...b). Jangan pilih yang ini lagi, karena tidak pernah benar-benar menjawab pertanyaan, tapi itu adalah hack 2015 di-hit-on-Google :)


Bagi mereka yang hanya mencari "memperpanjang array JavaScript" dan sampai di sini, Anda dapat menggunakannya dengan sangat baik Array.concat.

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

Concat akan mengembalikan salinan array baru, karena thread starter tidak mau. Tapi Anda mungkin tidak peduli (tentu untuk sebagian besar penggunaan ini tidak masalah).


Ada juga beberapa gula ECMAScript 6 yang bagus untuk ini dalam bentuk operator spread:

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

(Ini juga salinan.)


43
Pertanyaannya jelas menyatakan: "tanpa membuat array baru?"
Layu

10
@Wilt: Ini benar, jawabannya mengatakan mengapa :) Ini pertama kali dipukul di Google. Juga solusi lain jelek, menginginkan sesuatu yang ideomatis dan menyenangkan. Meskipun saat arr.push(...arr2)ini lebih baru dan lebih baik dan jawaban yang benar secara teknis untuk pertanyaan khusus ini.
odinho

7
Saya mendengar Anda, tetapi saya mencari "javascript append array tanpa membuat array baru" , maka sedih melihat bahwa 60 kali jawaban tervvotasikan yang tinggi tidak menjawab pertanyaan yang sebenarnya;) Saya tidak downvote, karena Anda membuatnya jelas dalam jawaban Anda.
Layu

202

Anda harus menggunakan teknik berbasis loop. Jawaban lain pada halaman ini yang didasarkan pada penggunaan .applydapat gagal untuk array besar.

Implementasi berbasis loop yang cukup singkat adalah:

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

Anda kemudian dapat melakukan hal berikut:

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

Jawaban DzinX (menggunakan push.apply) dan .applymetode berbasis lainnya gagal ketika array yang kita tambahkan besar (tes menunjukkan bahwa bagi saya besar adalah> 150.000 entri kira-kira di Chrome, dan> 500.000 entri di Firefox). Anda dapat melihat kesalahan ini terjadi di jsperf ini .

Terjadi kesalahan karena ukuran tumpukan panggilan terlampaui ketika 'Function.prototype.apply' dipanggil dengan array besar sebagai argumen kedua. (MDN memiliki catatan tentang bahaya melebihi ukuran tumpukan panggilan menggunakan Function.prototype.apply - lihat bagian berjudul "Terapkan dan fungsi bawaan ".)

Untuk perbandingan kecepatan dengan jawaban lain di halaman ini, lihat jsperf ini (terima kasih kepada EaterOfCode). Implementasi berbasis loop serupa dalam kecepatan untuk menggunakan Array.push.apply, tetapi cenderung sedikit lebih lambat daripada Array.slice.apply.

Menariknya, jika array yang Anda tambahkan jarang, forEachmetode berbasis di atas dapat mengambil keuntungan dari sparsity dan mengungguli .applymetode berbasis; lihat jsperf ini jika Anda ingin menguji sendiri.

By the way, jangan tergoda (seperti saya!) Untuk lebih mempersingkat implementasi forEach ke:

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

karena ini menghasilkan hasil sampah! Mengapa? Karena Array.prototype.forEachmenyediakan tiga argumen untuk fungsi yang dipanggil - ini adalah: (element_value, element_index, source_array). Semua ini akan didorong ke array pertama Anda untuk setiap iterasi forEachjika Anda menggunakan "forEach (this.push, this)"!


1
ps untuk menguji apakah other_array benar-benar array, pilih salah satu opsi yang dijelaskan di sini: stackoverflow.com/questions/767486/…
jcdude

6
Jawaban yang bagus. Saya tidak menyadari masalah ini. Saya mengutip jawaban Anda di sini: stackoverflow.com/a/4156156/96100
Tim Down

2
.push.applysebenarnya jauh lebih cepat daripada .forEachdi v8, yang tercepat masih merupakan inline loop.
Benjamin Gruenbaum

1
@BenjaminGruenbaum - dapatkah Anda memposting tautan ke beberapa hasil yang menunjukkan hal itu? Sejauh yang saya bisa lihat dari hasil yang dikumpulkan di jsperf yang tertaut di akhir komentar ini, menggunakan .forEachlebih cepat daripada .push.applydi Chrome / Chromium (di semua versi sejak v25). Saya belum dapat menguji v8 secara terpisah, tetapi jika Anda memiliki harap tautkan hasil Anda. Lihat jsperf: jsperf.com/array-extending-push-vs-concat/5
jcdude

1
@jcdude jsperf Anda tidak valid. karena semua elemen dalam array Anda undefined, maka .forEachakan melewatkannya, menjadikannya yang tercepat. .splicesebenarnya yang tercepat ?! jsperf.com/array-extending-push-vs-concat/18
EaterOfCode

153

Saya merasa yang paling elegan hari ini adalah:

arr1.push(...arr2);

The Artikel MDN pada operator penyebaran menyebutkan ini cara manis yang bagus di ES2015 (ES6):

Dorongan yang lebih baik

Contoh: push sering digunakan untuk mendorong array ke akhir array yang ada. Dalam ES5 ini sering dilakukan sebagai:

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

Dalam ES6 dengan spread, ini menjadi:

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

Perhatikan bahwa arr2tidak boleh besar (simpan di bawah sekitar 100.000 item), karena tumpukan panggilan meluap, sesuai jawaban jcdude.


1
Hanya peringatan dari kesalahan yang baru saja aku buat. Versi ES6 sangat dekat arr1.push(arr2). Ini bisa menjadi masalah seperti sehingga arr1 = []; arr2=['a', 'b', 'd']; arr1.push(arr2)hasilnya menjadi array array arr1 == [['a','b','d']]daripada dua array yang digabungkan. Ini adalah kesalahan yang mudah dibuat. Saya lebih suka jawaban kedua Anda di bawah ini karena alasan ini. stackoverflow.com/a/31521404/4808079
Seph Reed

Ada kenyamanan saat menggunakan .concatadalah bahwa argumen kedua tidak boleh berupa array. Ini dapat dicapai dengan menggabungkan operator spread dengan concat, mis.arr1.push(...[].concat(arrayOrSingleItem))
Steven Pribilinskiy

Ini adalah Javascript modern dan elegan untuk kasus-kasus di mana array target tidak kosong.
timbo

Apakah ini cukup cepat?
Roel

@RoelDelosReyes: Ya.
odinho

46

Pertama beberapa kata tentang apply()dalam JavaScript untuk membantu memahami mengapa kami menggunakannya:

The apply()metode panggilan fungsi dengan diberikan thisnilai, dan argumen yang disediakan sebagai array.

Push mengharapkan daftar item untuk ditambahkan ke array. The apply()metode, bagaimanapun, mengambil argumen diharapkan untuk panggilan fungsi sebagai array. Ini memungkinkan kita untuk dengan mudah pushelemen dari satu array ke array lain dengan push()metode bawaan .

Bayangkan Anda memiliki susunan ini:

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

dan cukup lakukan ini:

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

Hasilnya adalah:

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

Hal yang sama dapat dilakukan di ES6 menggunakan operator spread (" ...") seperti ini:

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

Lebih pendek dan lebih baik tetapi tidak sepenuhnya didukung di semua browser saat ini.

Juga jika Anda ingin memindahkan semuanya dari array bke a, mengosongkan bdalam proses, Anda dapat melakukan ini:

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

dan hasilnya adalah sebagai berikut:

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

1
atau while (b.length) { a.push(b.shift()); }benar?
LarsW

37

Jika Anda ingin menggunakan jQuery, ada $ .merge ()

Contoh:

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

Hasil: a = [1, 2, 3, 4, 5]


1
Bagaimana saya bisa menemukan implementasi (dalam kode sumber) jQuery.merge()?
ma11hew28

Butuh waktu 10 menit - jQuery adalah sumber terbuka dan sumber dipublikasikan di Github. Saya melakukan pencarian untuk "menggabungkan" dan selanjutnya menemukan apa yang tampaknya menjadi definisi fungsi gabungan dalam file core.js, lihat: github.com/jquery/jquery/jquery/blob/master/src/core.js#L331
amn

19

Saya suka a.push.apply(a, b)metode yang dijelaskan di atas, dan jika mau, Anda selalu dapat membuat fungsi perpustakaan seperti ini:

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

dan gunakan seperti ini

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

a.append(b)

6
Metode push.apply tidak boleh digunakan karena dapat menyebabkan stack overflow (dan karena itu gagal) jika argumen "array" Anda adalah array besar (mis. ~ 150000 entri di Chrome). Anda harus menggunakan "array.forEach" - lihat jawaban saya: stackoverflow.com/a/17368101/1280629
jcdude

12

Dimungkinkan untuk melakukannya menggunakan splice():

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

Tetapi meskipun lebih jelek itu tidak lebih cepat daripada push.apply, setidaknya tidak di Firefox 3.0.


9
Saya telah menemukan hal yang sama, sambungan tidak memberikan peningkatan kinerja untuk mendorong setiap item hingga sekitar 10.000 elemen array jsperf.com/splice-vs-push
Drew

1
+1 Terima kasih telah menambahkan ini dan perbandingan kinerja, menyelamatkan saya dari upaya menguji metode ini.
jjrv

1
Menggunakan salah satu splice.apply atau push.apply bisa gagal karena stack overflow jika array b besar. Mereka juga lebih lambat daripada menggunakan loop "for" atau "forEach" - lihat jsPerf ini: jsperf.com/array-extending-push-vs-concat/5 dan jawaban saya stackoverflow.com/questions/1374126/…
jcdude

4

Solusi ini cocok untuk saya (menggunakan operator spread dari 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

Anda dapat membuat polyfill untuk ekstensi seperti yang saya miliki di bawah ini. Ini akan menambah array; di tempat dan kembalikan sendiri, sehingga Anda dapat memadukan metode lain.

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

Menggabungkan jawaban ...

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
Tidak, tidak perlu melakukan ini. A untuk loop menggunakan forEach lebih cepat daripada menggunakan push.apply, dan bekerja tidak peduli berapa panjang array untuk memperpanjang. Lihat jawaban saya yang telah direvisi: stackoverflow.com/a/17368101/1280629 Bagaimanapun, bagaimana Anda tahu bahwa 150000 adalah angka yang tepat untuk digunakan untuk semua browser? Ini adalah fudge.
jcdude

lol. jawaban saya tidak dapat dikenali, tetapi tampaknya merupakan kombinasi dari yang lain yang ditemukan pada saat itu - yaitu ringkasan. jangan khawatir
zCoder

0

Solusi lain untuk menggabungkan lebih dari dua array

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

Kemudian panggil dan cetak sebagai:

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

Output akan menjadi: Array [1, 2, 3, 4, 5, 6, 7]


-1

Jawabannya sangat sederhana.

>>> 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 bertindak sangat mirip dengan penggabungan string JavaScript. Ini akan mengembalikan kombinasi parameter yang Anda masukkan ke fungsi concat di akhir array yang Anda panggil fungsi. Intinya adalah bahwa Anda harus menetapkan nilai yang dikembalikan ke variabel atau hilang. Jadi misalnya

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

2
Seperti yang dinyatakan dengan jelas dalam dokumen untuk Array.prototype.concat()di MDN ini akan mengembalikan Array baru , itu tidak akan menambahkan ke array yang ada seperti OP jelas bertanya.
Layu

-2

Gunakan Array.extendalih-alih Array.pushuntuk> 150.000 rekaman.

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

Sangat sederhana, tidak mengandalkan operator yang tersebar atau berlaku, jika itu masalah.

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

Setelah menjalankan beberapa tes kinerja, ini sangat lambat, tetapi menjawab pertanyaan sehubungan dengan tidak membuat array baru. Concat secara signifikan lebih cepat, bahkan jQuery $.merge()mengatasinya.

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


4
Anda harus menggunakan .forEachdaripada .mapjika Anda tidak menggunakan nilai kembali. Lebih baik menyampaikan maksud kode Anda.
david

@david - untuk masing-masing. Saya lebih suka synax .maptetapi Anda juga bisa melakukannya b.forEach(function(x) { a.push(x)} ). Sebenarnya saya menambahkan itu ke tes jsperf dan itu rambut lebih cepat dari peta. Masih sangat lambat.
elPastor

3
Ini bukan kasus preferensi. Metode 'fungsional' ini masing-masing memiliki makna spesifik yang terkait dengannya. Menelepon ke .mapsini terlihat sangat aneh. Itu seperti menelepon b.filter(x => a.push(x)). Ini berfungsi, tetapi membingungkan pembaca dengan menyiratkan sesuatu sedang terjadi padahal tidak.
david

2
@ ElPastor, itu sepadan dengan waktu Anda, karena beberapa pria lain seperti saya akan duduk dan menggaruk tengkuknya bertanya-tanya apa lagi yang terjadi di kode Anda yang tidak dapat dilihatnya. Dalam kebanyakan kasus biasa, kejelasan niatlah yang penting, bukan kinerja. Harap hormati waktu mereka yang membaca kode Anda, karena mereka mungkin banyak ketika Anda hanya satu. Terima kasih.
Dmytro Y.

1
@ ElPastor Saya tahu persis apa yang .map()terjadi dan itu masalahnya. Pengetahuan ini akan membuat saya berpikir Anda memerlukan array yang dihasilkan, jika tidak maka akan menjadi hal yang baik untuk digunakan .forEach()untuk membuat pembaca berhati-hati tentang efek samping dari fungsi callback. Secara tegas, solusi Anda menciptakan array tambahan bilangan asli (hasil push), sedangkan pertanyaannya menyatakan: "tanpa membuat array baru".
Dmytro Y.
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.