Di Ruby, diberikan array dalam salah satu bentuk berikut ...
[apple, 1, banana, 2]
[[apple, 1], [banana, 2]]
... apa cara terbaik untuk mengubahnya menjadi hash dalam bentuk ...
{apple => 1, banana => 2}
CATATAN : Untuk solusi yang ringkas dan efisien, silakan lihat jawaban Marc-André Lafortune di bawah ini.
Jawaban ini awalnya ditawarkan sebagai alternatif pendekatan yang menggunakan flatten, yang paling banyak dipilih pada saat penulisan. Saya harus mengklarifikasi bahwa saya tidak bermaksud menyajikan contoh ini sebagai praktik terbaik atau pendekatan yang efisien. Berikut jawaban asli.
Peringatan! Solusi yang menggunakan flatten tidak akan mempertahankan kunci atau nilai Array!
Berdasarkan jawaban populer @John Topley, mari kita coba:
a3 = [ ['apple', 1], ['banana', 2], [['orange','seedless'], 3] ]
h3 = Hash[*a3.flatten]
Ini melempar kesalahan:
ArgumentError: odd number of arguments for Hash
from (irb):10:in `[]'
from (irb):10
Konstruktor mengharapkan Array dengan panjang genap (misalnya ['k1', 'v1,' k2 ',' v2 ']). Yang lebih buruk adalah bahwa Array berbeda yang diratakan dengan panjang yang rata hanya akan memberi kita Hash dengan nilai yang salah.
Jika Anda ingin menggunakan kunci atau nilai Array, Anda dapat menggunakan map :
h3 = Hash[a3.map {|key, value| [key, value]}]
puts "h3: #{h3.inspect}"
Ini mempertahankan kunci Array:
h3: {["orange", "seedless"]=>3, "apple"=>1, "banana"=>2}
h3 = Hash[*a3.flatten(1)]
bukannyah3 = Hash[*a3.flatten]
akan melempar kesalahan.to_h
lebih baik.Cukup gunakan
Hash[*array_variable.flatten]
Sebagai contoh:
Penggunaan
Array#flatten(1)
membatasi rekursi sehinggaArray
kunci dan nilai berfungsi seperti yang diharapkan.sumber
Hash[*ary.flatten(1)]
, yang akan mempertahankan kunci dan nilai array. Itu rekursifflatten
yang menghancurkan mereka, yang cukup mudah untuk dihindari.Cara terbaik adalah dengan menggunakan
Array#to_h
:Perhatikan bahwa
to_h
juga menerima blok:Catatan :
to_h
menerima blok di Ruby 2.6.0+; untuk batu rubi awal Anda dapat menggunakanbackports
permata saya danrequire 'backports/2.6.0/enumerable/to_h'
to_h
tanpa blok diperkenalkan di Ruby 2.1.0.Sebelum Ruby 2.1, seseorang dapat menggunakan yang kurang terbaca
Hash[]
:Akhirnya, berhati-hatilah dengan solusi apa pun yang digunakan
flatten
, ini dapat menimbulkan masalah dengan nilai yang merupakan array itu sendiri.sumber
to_h
metode ini lebih baik daripada jawaban di atas karena metode ini mengungkapkan maksud dari konversi setelah beroperasi pada array.Array#to_h
maupunEnumerable#to_h
dalam inti ruby 1,9.[[apple, 1], [banana, 2], [apple, 3], [banana, 4]]
dan saya ingin outputnya sebagai{"apple" =>[1,3], "banana"=>[2,4]}
?Memperbarui
Ruby 2.1.0 dirilis hari ini . Dan saya datang dengan
Array#to_h
( catatan rilis dan ruby-doc ), yang memecahkan masalah mengonversiArray
file menjadiHash
.Contoh dokumen Ruby:
sumber
Bentuk kedua lebih sederhana:
a = array, h = hash, r = return-value hash (yang kita kumpulkan), i = item dalam array
Cara paling rapi yang bisa saya pikirkan untuk melakukan bentuk pertama adalah seperti ini:
sumber
a.inject({})
satu baris yang memungkinkan penugasan nilai yang lebih fleksibel.h = {}
contoh kedua melalui penggunaan inject, berakhir dengana.each_slice(2).inject({}) { |h,i| h[i.first] = i.last; h }
a.each_slice(2).to_h
Anda juga dapat dengan mudah mengonversi larik 2D menjadi hash menggunakan:
sumber
Ringkasan & TL; DR:
Jawaban ini diharapkan menjadi rangkuman informasi yang komprehensif dari jawaban lain.
Versi yang sangat singkat, mengingat data dari pertanyaan ditambah beberapa tambahan:
Diskusi dan detail ikuti.
Penyiapan: variabel
Untuk menampilkan data yang akan kita gunakan di depan, saya akan membuat beberapa variabel untuk mewakili berbagai kemungkinan data. Mereka masuk ke dalam kategori berikut:
Berdasarkan apa yang langsung di pertanyakan, sebagai
a1
dana2
:(Catatan: Saya menganggap itu
apple
danbanana
dimaksudkan untuk mewakili variabel. Seperti yang telah dilakukan orang lain, saya akan menggunakan string mulai dari sini sehingga masukan dan hasil bisa cocok.)Kunci dan / atau nilai multi-nilai, seperti
a3
:Dalam beberapa jawaban lain, kemungkinan lain disajikan (yang saya kembangkan di sini) - kunci dan / atau nilai dapat berupa array sendiri:
Array tidak seimbang, seperti
a4
:Untuk ukuran yang baik, saya pikir saya akan menambahkan satu untuk kasus di mana kami mungkin memiliki masukan yang tidak lengkap:
Sekarang, untuk bekerja:
Dimulai dengan sebuah array awalnya-datar,
a1
:Beberapa orang menyarankan penggunaan
#to_h
(yang muncul di Ruby 2.1.0, dan dapat di- backport ke versi sebelumnya). Untuk array yang awalnya datar, ini tidak berfungsi:Menggunakan
Hash::[]
kombinasi dengan operator percikan tidak:Jadi itulah solusi untuk kasus sederhana yang direpresentasikan oleh
a1
.Dengan berbagai kunci array nilai pasangan /,
a2
:Dengan array
[key,value]
tipe array, ada dua cara untuk melakukannya.Pertama,
Hash::[]
masih berfungsi (seperti halnya dengan*a1
):Dan kemudian juga
#to_h
berfungsi sekarang:Jadi, dua jawaban mudah untuk kasus array bersarang sederhana.
Ini tetap benar bahkan dengan sub-array sebagai kunci atau nilai, seperti
a3
:Tetapi durian memiliki paku (struktur yang tidak normal memberikan masalah):
Jika kami mendapatkan data masukan yang tidak seimbang, kami akan mengalami masalah dengan
#to_h
:Tetapi
Hash::[]
masih berfungsi, hanya menetapkannil
sebagai nilai untukdurian
(dan elemen array lainnya di a4 yang hanya array 1-nilai):Perataan - menggunakan variabel baru
a5
dana6
Beberapa jawaban lain disebutkan
flatten
, dengan atau tanpa1
argumen, jadi mari buat beberapa variabel baru:Saya memilih untuk menggunakan
a4
sebagai data dasar karena masalah keseimbangan yang kami miliki, yang muncul dengana4.to_h
. Saya pikir meneleponflatten
mungkin salah satu pendekatan yang mungkin digunakan seseorang untuk mencoba menyelesaikannya, yang mungkin terlihat seperti berikut ini.flatten
tanpa argumen (a5
):Sekilas naif, ini muncul untuk bekerja - tetapi turun kami di kaki salah dengan jeruk tanpa biji, dengan demikian juga membuat
3
sebuah kunci dandurian
sebuah nilai .Dan ini, seperti
a1
, tidak berhasil:Jadi
a4.flatten
tidak berguna bagi kami, kami hanya ingin menggunakanHash[a4]
The
flatten(1)
kasus (a6
):Tapi bagaimana dengan hanya merata sebagian? Perlu dicatat bahwa memanggil
Hash::[]
menggunakansplat
pada array yang diratakan sebagian (a6
) tidak sama dengan memanggilHash[a4]
:Array yang sudah diratakan, masih bersarang (cara alternatif untuk mendapatkannya
a6
):Tapi bagaimana jika ini adalah cara pertama kita mendapatkan array? (Artinya, sebanding dengan
a1
, itu adalah data masukan kami - kali ini beberapa datanya dapat berupa larik atau objek lain.) Kami telah melihat ituHash[*a6]
tidak berfungsi, tetapi bagaimana jika kami masih ingin mendapatkan perilaku di mana elemen terakhir (penting! lihat di bawah) bertindak sebagai kunci untuknil
nilai?Dalam situasi seperti ini, masih ada cara untuk melakukan ini, menggunakan
Enumerable#each_slice
untuk kembali ke pasangan kunci / nilai sebagai elemen dalam larik terluar:Perhatikan bahwa ini akhirnya memberi kita array baru yang tidak " identik "
a4
, tetapi memiliki nilai yang sama :Dan dengan demikian kita dapat kembali menggunakan
Hash::[]
:Tapi ada masalah!
Penting untuk dicatat bahwa
each_slice(2)
solusinya hanya mengembalikan semuanya ke kewarasan jika kunci terakhir adalah yang kehilangan nilainya. Jika nanti kami menambahkan pasangan kunci / nilai ekstra:Dan dua hash yang kami dapatkan dari sini berbeda dalam hal-hal penting:
(Catatan: Saya menggunakan
awesome_print
'sap
. Hanya untuk membuatnya lebih mudah untuk menunjukkan struktur di sini, tidak ada persyaratan konseptual untuk ini)Jadi
each_slice
solusi untuk input datar tidak seimbang hanya berfungsi jika bit tidak seimbang berada di bagian paling akhir.Take-aways:
[key, value]
pasangan (sub-larik untuk setiap item di larik terluar).#to_h
atauHash::[]
keduanya akan berhasil.Hash::[]
kombinasi dengan splat (*
) akan berfungsi, selama input seimbang .value
adalah satu-satunya yang hilang.Catatan tambahan: Saya memposting jawaban ini karena saya merasa ada nilai yang bisa ditambahkan - beberapa jawaban yang ada memiliki informasi yang salah, dan tidak ada (yang saya baca) memberikan jawaban selengkap yang saya coba lakukan di sini. Saya harap ini membantu. Meskipun demikian, saya berterima kasih kepada mereka yang datang sebelum saya, beberapa di antaranya memberikan inspirasi untuk sebagian jawaban ini.
sumber
Menambahkan jawaban tetapi menggunakan array dan anotasi anonim:
Membongkar jawaban itu, mulai dari dalam:
"a,b,c,d"
sebenarnya adalah sebuah string.split
dengan koma menjadi sebuah larik.zip
itu bersama dengan array berikut.[1,2,3,4]
adalah larik sebenarnya.Hasil antara adalah:
flatten kemudian mengubahnya menjadi:
lalu:
*["a",1,"b",2,"c",3,"d",4]
buka gulungannya"a",1,"b",2,"c",3,"d",4
yang bisa kita gunakan sebagai argumen untuk
Hash[]
metode ini:yang menghasilkan:
sumber
*
) dan flatten:Hash[("a,b,c,d".split(',').zip([1,2,3,4]))]
=>{"a"=>1, "b"=>2, "c"=>3, "d"=>4}
. Lebih detail dalam jawaban yang saya tambahkan.jika Anda memiliki larik yang terlihat seperti ini -
dan Anda ingin elemen pertama dari setiap array menjadi kunci untuk hash dan elemen lainnya menjadi array nilai, lalu Anda dapat melakukan sesuatu seperti ini -
sumber
Tidak yakin apakah ini cara terbaik, tetapi ini berhasil:
sumber
Jika nilai numerik adalah indeks seq, maka kita bisa memiliki cara yang lebih sederhana ... Ini kiriman kode saya, Ruby saya agak berkarat
sumber