Rails memetakan array hash ke dalam satu hash

94

Saya memiliki serangkaian hash seperti ini:

 [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

Dan saya mencoba memetakan ini ke dalam satu hash seperti ini:

{"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Saya telah mencapainya dengan menggunakan

  par={}
  mitem["params"].each { |h| h.each {|k,v| par[k]=v} } 

Tapi saya bertanya-tanya apakah mungkin melakukan ini dengan cara yang lebih idiomatis (sebaiknya tanpa menggunakan variabel lokal).

Bagaimana saya bisa melakukan ini?

Bart Platak
sumber

Jawaban:

164

Anda dapat membuat Enumerable#reducedan Hash#mergemencapai apa yang Anda inginkan.

input = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
input.reduce({}, :merge)
  is {"testPARAM2"=>"testVAL2", "testPARAM1"=>"testVAL1"}

Mengurangi semacam array seperti menempelkan panggilan metode di antara setiap elemennya.

Misalnya [1, 2, 3].reduce(0, :+)seperti mengatakan 0 + 1 + 2 + 3dan memberi 6.

Dalam kasus kami, kami melakukan sesuatu yang serupa, tetapi dengan fungsi penggabungan, yang menggabungkan dua hash.

[{:a => 1}, {:b => 2}, {:c => 3}].reduce({}, :merge)
  is {}.merge({:a => 1}.merge({:b => 2}.merge({:c => 3})))
  is {:a => 1, :b => 2, :c => 3}
cjhveal.dll
sumber
1
Terima kasih, ini jawaban yang bagus :) Dijelaskan dengan sangat baik!
Bart Platak
42
input.reduce (&: merge) sudah cukup.
redgetan
@redgetan apakah itu berbeda dari input.reduce(:merge)?
David van Geest
1
@David van Geest: Dalam hal ini mereka setara. Unary ampersand seperti yang digunakan di sini membentuk blok dari simbol. Namun, reduksi memiliki kasus khusus yang menerima simbol. Saya ingin menghindari operator ampersand unary untuk menyederhanakan contoh, tetapi redgetan benar bahwa nilai awal adalah opsional dalam kasus ini.
cjhveal
1
Perhatikan bahwa jika Anda menggunakan, merge!bukan mergeitu akan mengubah hash pertama (yang mungkin tidak Anda inginkan) tetapi tidak akan membuat hash perantara untuk setiap penggabungan baru.
Phrogz
51

Bagaimana tentang:

h = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
r = h.inject(:merge)
shigeya
sumber
Skema ini secara efektif sama dengan apa yang dijawab Joshua, tetapi berulang kali menerapkan #merge (nama metode diteruskan sebagai simbol) pada semua hash (anggap inject sebagai menyuntikkan operator antar item). Lihat #inject .
shigeya
2
Kenapa kita tidak membutuhkan ampersand, seperti di h.inject (&: merge)?
Donato
5
Karena metode inject menerima simbol sebagai parameter untuk diinterpretasikan sebagai nama metode juga. Ini fitur suntik.
shigeya
9

Gunakan #inject

hashes = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]
merged = hashes.inject({}) { |aggregate, hash| aggregate.merge hash }
merged # => {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Joshua Cheek
sumber
0

Di sini Anda dapat menggunakan injeksi atau pengurangan dari kelas Enumerable karena keduanya adalah alias satu sama lain sehingga tidak ada manfaat kinerja untuk keduanya.

 sample = [{"testPARAM1"=>"testVAL1"}, {"testPARAM2"=>"testVAL2"}]

 result1 = sample.reduce(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}

 result2 = sample.inject(:merge)
 # {"testPARAM1"=>"testVAL1", "testPARAM2"=>"testVAL2"}
Nikhil Mohadikar
sumber