Як я можу об'єднати два хеші без перезаписаних повторюваних ключів у Ruby?


140

Чи є простий чи елегантний спосіб об'єднати два хеши, не перезаписуючи повторювані ключі?

Тобто, якщо ключ присутній у вихідному хеші, я не хочу змінювати його значення.


Ви дійсно маєте на увазі масиви (наприклад: ['a', 'b', 'c']) або хеші (наприклад: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Алекс Рейснер

Вибачте, я говорив про хеши :)
Клаудіо Acciaresi

Відповіді:


232

Якщо у вас є два хеша, optionsі defaults, і ви хочете об'єднатись defaultsу них, optionsне перезаписуючи існуючі ключі, то, що ви дійсно хочете зробити, це зворотне: об'єднати optionsв defaults:

options = defaults.merge(options)

Або якщо ви використовуєте Rails, ви можете:

options.reverse_merge!(defaults)

Цілком погоджуюсь, велике спасибі за reverse_merge! метод не знав цього :)
Клаудіо Acciaresi

Чому тут потрібні дужки? Ви не можете просто зробити default.merge параметри, що з'являються.
Донато

1
Вони застаріли reverse_merge!через проблеми безпеки на рейках 5.1
Mirv - Matt

@ Mirv-Matt - Я не бачу повідомлення про амортизацію. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij

17

У стандартній бібліотеці Ruby є спосіб об'єднати хеші без перезапису існуючих значень або перепризначення хеша.

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

3

Якщо ваші проблеми полягають у тому, що оригінальний хеш і другий можуть мати повторювані ключі, і ви не хочете перезаписувати в жодному напрямку, можливо, вам доведеться скористатися простим ручним злиттям з якоюсь перевіркою та обробкою зіткнення:

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

Очевидно, що це дуже рудиментарно, і передбачається, що у hash1 немає жодних клавіш під назвою "hash2-походження - що б там не було" - вам може бути краще просто додати число до ключа, щоб воно стало key1, key2 і так далі, поки ви не натиснете на той, який вже не є хеш1. Крім того, я не робив жодного рубіну протягом декількох місяців, тому це, мабуть, не синтаксично правильно, але ви повинні мати можливість отримати суть.

Альтернативно, повторно визначте значення ключа у вигляді масиву, щоб hash1 [key] повертав початкове значення з hash1 та значення з hash2. Залежить, яким ви хочете, щоб ваш результат був насправді.


Як щодо того, якщо не зберігати обидві клавіші, а додавати значення одного ключа?
Том KC Chiu

1
@ TomK.C.Chiu Це дуже залежало б від обставин, про які ми не можемо судити з питання - що робити, якщо значення в hash1 - це рядки, а hash2 - цілі числа? У деяких випадках це може бути життєздатним варіантом, але частіше це спричинятиме проблеми - пропозиція використовувати списки для значень працює цілком чисто.
гленатрон

0

Тут ви можете з’єднати свої 2 хеші reverse_merge

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

user = {
  name: 'Jhon Doe',
  email: 'jhon.doe@gmail.com' 
}
newHash = oder.reverse_merge!(user)
render json: { data: newHash, status: 200 }

0

Якщо ви хочете об'єднати два хеші optionsта defaultsне перезаписати хеш місця призначення, ви можете перевірити, selectчи ключ уже присутній у хеші призначення. Ось чисте рішення Ruby без 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}

Або якщо ключ присутній, але містить nilі ви хочете його перезаписати:

options.merge!(defaults.select{ |k,_| options[k].nil? })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.