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?


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

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.


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

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.


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"]

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"]

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"]

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

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"]

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]

5
maaf ... tidak memperhatikan urutan tertentu yang Anda inginkan untuk keluarannya. Maaf karena mencoba membantu, tidak akan terjadi lagi.
David Morrow
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.