Bagaimana saya bisa menggabungkan dua hash tanpa kunci duplikat ditimpa di Ruby?

140

Apakah ada yang mudah atau elegan untuk menggabungkan dua hash tanpa menimpa kunci duplikat?

Yaitu, jika kuncinya ada dalam hash asli saya tidak ingin mengubah nilainya.

Claudio Acciaresi
sumber
Apakah maksud Anda array (misalnya: ['a', 'b', 'c']) atau hash (misalnya: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner
Maaf, saya sedang berbicara tentang hash :)
Claudio Acciaresi

Jawaban:

232

Jika Anda memiliki dua hash, optionsdan defaults, dan Anda ingin menggabungkan defaultske dalam optionstanpa Timpa kunci yang ada, apa yang Anda benar-benar ingin lakukan adalah kebalikan: merge optionske defaults:

options = defaults.merge(options)

Atau, jika Anda menggunakan Rails yang dapat Anda lakukan:

options.reverse_merge!(defaults)
Alex Reisner
sumber
Sepenuhnya setuju, terima kasih banyak untuk reverse_merge! metode tidak tahu itu :)
Claudio Acciaresi
MENGAPA tanda kurung diperlukan di sini? Anda tidak bisa melakukan default.merge opsi yang muncul.
Donato
1
Mereka sudah usang reverse_merge!karena masalah keamanan di rel 5.1
Mirv - Matt
@ Mirv-Matt - Saya tidak melihat pemberitahuan penyusutan. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij
17

Ada cara di pustaka Ruby standar untuk menggabungkan Hash tanpa menimpa nilai yang ada atau menugaskan kembali hash.

important_hash.merge!(defaults) { |key, important, default| important }
ujifgc
sumber
3

Jika masalah Anda adalah bahwa hash asli dan yang kedua mungkin memiliki kunci duplikat dan Anda tidak ingin menimpa di kedua arah, Anda mungkin harus menggunakan penggabungan manual sederhana dengan semacam pemeriksaan dan penanganan tabrakan:

hash2.each_key do |key|
  if ( hash1.has_key?(key) )
       hash1[ "hash2-originated-#{key}" ] = hash2[key]
  else
       hash1[key]=hash2[key]
  end
end

Jelas, ini sangat sederhana dan mengasumsikan bahwa hash1 tidak memiliki kunci yang disebut "hash2-originated-whatever" - Anda mungkin lebih baik hanya menambahkan angka pada kunci sehingga menjadi key1, key2 dan seterusnya sampai Anda menekan salah satu yang belum ada di hash1. Juga, saya belum melakukan ruby ​​selama beberapa bulan sehingga mungkin secara sintaksis tidak benar, tetapi Anda harus bisa mendapatkan intinya.

Alternatifnya mendefinisikan kembali nilai kunci sebagai array sehingga hash1 [kunci] mengembalikan nilai asli dari hash1 dan nilai dari hash2. Tergantung apa yang Anda inginkan, hasil Anda sebenarnya.

glenatron
sumber
Bagaimana jika tidak menyimpan kedua kunci, tetapi menambahkan nilai-nilai dari kunci yang sama?
Tom KC Chiu
1
@ TomK.C.Chiu Itu akan sangat tergantung pada keadaan yang kita tidak bisa menilai dari pertanyaan - bagaimana jika nilai-nilai dalam hash1 adalah string dan hash2 adalah bilangan bulat? Untuk beberapa kasus yang mungkin menjadi opsi yang layak, tetapi lebih sering itu akan menyebabkan masalah - saran menggunakan daftar untuk nilai-nilai bekerja dengan sangat bersih.
glenatron
0

Di sini Anda dapat menggabungkan 2 hash Anda dengan reverse_merge

order = {
 id: 33987,
 platform: 'web'
}

user = {
  name: 'Jhon Doe',
  email: '[email protected]' 
}
newHash = oder.reverse_merge!(user)
render json: { data: newHash, status: 200 }
Daniel Agus Sidabutar
sumber
0

Jika Anda ingin menggabungkan kedua hash optionsdan defaultstanpa menimpa hash tujuan, Anda dapat memeriksa selectapakah kunci sudah ada di hash tujuan. Inilah solusi Ruby murni tanpa Rails:

options  = { "a" => 100, "b" => 200 }
defaults = { "b" => 254, "c" => 300 }
options.merge!(defaults.select{ |k,_| not options.has_key? k })

# output
# => {"a"=>100, "b"=>200, "c"=>300}

Atau jika kunci ada, tetapi berisi nildan Anda ingin menimpanya:

options.merge!(defaults.select{ |k,_| options[k].nil? })
Kristen
sumber