Gabungkan dan interleave dua array di Ruby

106

Saya memiliki kode berikut:

a = ["Cat", "Dog", "Mouse"]
s = ["and", "&"]

Saya ingin menggabungkan array smenjadi array ayang akan memberi saya:

["Cat", "and", "Dog", "&", "Mouse"]

Melihat melalui Ruby Array dan dokumen Enumerable, saya tidak melihat metode seperti itu yang akan mencapai ini.

Apakah ada cara agar saya dapat melakukan ini tanpa melakukan iterasi melalui setiap array?

Chris Ledet
sumber
a akan selalu memiliki 3 elemen dan s dua? beberapa contoh lagi akan berguna.
tokland

Jawaban:

171

Anda dapat melakukannya dengan:

a.zip(s).flatten.compact
DigitalRoss
sumber
4
Bagaimana jika amemiliki lebih dari 3 elemen?
Michael Kohl
116
["a", "b"] .concat (["c", "d"]) # => ["a", "b", "c", "d"]
Leo Romanovsky
30
@ Leo, @chuck: jika Anda membaca contoh ini, Anda akan melihat bahwa Chris ingin menyisipkan elemen, bukan menggabungkannya . Pada dasarnya, dia menginginkan [a, s].transposekecuali bahwa dua baris tidak sesuai, meninggalkan #zipsebagai solusi yang jelas. Dan saya tidak percaya maksudnya bahwa dia benar-benar peduli apakah abermutasi ... Saya tidak berpikir dia mengomentari solusi mutasi vs fungsional sama sekali, dia hanya mencoba mendeskripsikan polanya.
DigitalRoss
15
1 karena menjadi satu-satunya orang yang benar-benar membaca pertanyaan blummin! > _ <
Matt Fletcher
5
Lebih penting lagi, bagaimana jika kedua larik memiliki panjang yang tidak sama? Apalagi jika s yang lebih panjang? Saya rasa saya dapat dengan aman berasumsi contoh yang Chris berikan bukanlah data aktual yang dia kerjakan. pertimbangkan: [] .zip [1, 2] => nihil (akan kesulitan memanggil #flatten pada itu) [3,4] .zip ([1, 3, 5, 7]) => [[3 , 1], [4, 3]] (Ups, kira kami tidak peduli dengan beberapa elemen terakhir dalam larik ke-2)
hoff2
32

Ini tidak akan memberikan larik hasil dalam urutan yang diminta Chris, tetapi jika urutan larik yang dihasilkan tidak penting, Anda bisa menggunakan saja a |= b. Jika Anda tidak ingin bermutasi a, Anda dapat menulis a | bdan menetapkan hasilnya ke variabel.

Lihat dokumentasi set union untuk kelas Array di http://www.ruby-doc.org/core/classes/Array.html#M000275 .

Jawaban ini mengasumsikan bahwa Anda tidak menginginkan elemen array duplikat. Jika Anda ingin mengizinkan elemen duplikat dalam larik terakhir Anda, a += blakukan triknya. Sekali lagi, jika Anda tidak ingin bermutasi a, gunakan a + bdan tetapkan hasilnya ke variabel.

Menanggapi beberapa komentar di halaman ini, kedua solusi ini akan bekerja dengan array dalam berbagai ukuran.

Michael Stalker
sumber
Yang satu ini sepertinya yang terbaik.
ardavis
11
Ini memberi ["Cat", "Dog", "Mouse", "and", "&"], yang bukan diinginkan OP.
Andrew Grimm
Panggilan yang bagus, Andrew. Saya akan memperbarui jawaban saya untuk mengatakan bahwa saya tidak menjawab pertanyaan Chris.
Michael Stalker
29

Jika Anda tidak ingin duplikat, mengapa tidak menggunakan operator union :

new_array = a | s
Douglas
sumber
1
Memberikan +1 untuk solusi elegan, sederhana, dan diremehkan.
Giacomo1968
Tentu saja itu menjawab pertanyaan itu! Pertanyaannya adalah: "Saya ingin menggabungkan larik menjadi larik a"
Douglas
Solusi bagus - tetapi ini mengubah urutan hasil. Hasil dari sakan berada di akhir larik baru.
Hendrik
1
Urutan elemen tidak akan seperti yang diinginkan OP.
tokland
6
s.inject(a, :<<)

s   #=> ["and", "&"]
a   #=> ["Cat", "Dog", "Mouse", "and", "&"]

Itu tidak memberi Anda pesanan yang Anda minta, tetapi itu cara yang bagus untuk menggabungkan dua larik dengan menambahkan ke satu.


sumber
Saya suka, pendek dan bersih. :)
Nafaa Boutefer
6

Berikut adalah solusi yang memungkinkan interleaving beberapa array dengan ukuran berbeda (solusi umum):

arr = [["Cat", "Dog", "Mouse", "boo", "zoo"],
 ["and", "&"],
 ["hello", "there", "you"]]

first, *rest = *arr; first.zip(*rest).flatten.compact
=> ["Cat", "and", "hello", "Dog", "&", "there", "Mouse", "you", "boo", "zoo"]
Abdo
sumber
2
Bagus! Satu batasan, larik pertama harus yang terpanjang.
Brian Low
@BrianLow tangkapan yang bagus!
Abdo
5

Ini tidak terlalu elegan, tetapi berfungsi untuk array dengan ukuran berapa pun:

>> a.map.with_index { |x, i| [x, i == a.size - 2 ? s.last : s.first] }.flatten[0..-2] 
#=> ["Cat", "and", "Dog", "&", "Mouse"]
Michael Kohl
sumber
1 untuk menangani kasus tepi yang aneh, saya pikir i = s.cycle; a.map { |x| [x, i.next] }.flatten[0..-2]akan sama validnya.
mu terlalu pendek
Saya tidak yakin apakah OP ingin berganti anddan &, jadi saya menganggapnya sesingkat mungkin, sambil membiarkannya aberapa lama.
Michael Kohl
3

Bagaimana dengan solusi yang lebih umum yang berfungsi bahkan jika array pertama bukan yang terpanjang dan menerima sejumlah array?

a = [
    ["and", "&"],
    ["Cat", "Dog", "Mouse"]
]

b = a.max_by(&:length)
a -= [b]
b.zip(*a).flatten.compact

 => ["Cat", "and", "Dog", "&", "Mouse"]
Mike
sumber
2

Untuk menangani situasi di mana a& stidak memiliki ukuran yang sama:

a.zip(s).flatten.compact | s
  • .compactakan dihapus nilbila alebih besar daris
  • | sakan menambahkan item yang tersisa dari ssaat alebih kecil daris
Shubham Chaudhary
sumber
1

Salah satu cara untuk melakukan interleave dan juga menjamin array mana yang terbesar untuk metode zip, adalah dengan mengisi salah satu array nilhingga ukuran array lainnya. Dengan cara ini, Anda juga menjamin elemen mana dari array mana yang akan berada di posisi pertama:

preferred_arr = ["Cat", "Dog", "Mouse"]
other_arr = ["and","&","are","great","friends"]

preferred_arr << nil while preferred_arr.length < other_arr.length
preferred_arr.zip(other_arr).flatten.compact
#=> ["Cat", "and", "Dog", "&", "Mouse", "are", "great", "friends"]
Joao Cunha
sumber
1
Sedikit lebih baik: preferred_arr.zip(other_arr).flatten | other_arr(tanpa pengisian ulang nihil)
Adam Fendley
-2
arr = [0, 1]
arr + [2, 3, 4]

//outputs [0, 1, 2, 3, 4]
David Morrow
sumber
5
maaf ... tidak memperhatikan urutan tertentu yang Anda inginkan untuk keluarannya. Maaf karena mencoba membantu, tidak akan terjadi lagi.
David Morrow