Jak mogę połączyć dwa skróty bez nadpisywania zduplikowanych kluczy w Rubim?

140

Czy istnieje łatwy lub elegancki sposób łączenia dwóch skrótów bez nadpisywania zduplikowanych kluczy?

Oznacza to, że jeśli klucz jest obecny w oryginalnym skrócie, nie chcę zmieniać jego wartości.

Claudio Acciaresi
źródło
Czy naprawdę masz na myśli tablice (np .: ['a', 'b', 'c']) czy hashe (np .: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner
Przepraszam, mówiłem o
hashach

Odpowiedzi:

232

Jeśli masz dwa hashe, optionsi defaults, i chcesz połączyć defaultssię optionsbez nadpisywania istniejących kluczy, co naprawdę chcesz robić jest odwrotna: scalanie optionsw defaults:

options = defaults.merge(options)

Lub, jeśli używasz Railsów, możesz:

options.reverse_merge!(defaults)
Alex Reisner
źródło
Całkowicie się zgadzam, wielkie dzięki za reverse_merge! metoda tego nie wiedziała :)
Claudio Acciaresi
Dlaczego potrzebne są tutaj nawiasy? Nie możesz po prostu zrobić opcji default.merge, które się pojawi.
Donato
1
Są przestarzałe z reverse_merge!powodu problemów z bezpieczeństwem w szynach 5.1
Mirv - Matt
@ Mirv-Matt - nie widzę powiadomienia o amortyzacji. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij
17

W standardowej bibliotece Ruby istnieje sposób na scalenie skrótów bez nadpisywania istniejących wartości lub ponownego przypisywania skrótu.

important_hash.merge!(defaults) { |key, important, default| important }
ujifgc
źródło
3

Jeśli problem polega na tym, że oryginalny hash i drugi mogą mieć zduplikowane klucze i nie chcesz nadpisywać w żadnym kierunku, być może będziesz musiał przejść do prostego ręcznego scalania z pewnego rodzaju sprawdzaniem i obsługą kolizji:

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

Oczywiście jest to bardzo elementarne i zakłada, że ​​hash1 nie ma żadnych kluczy zwanych „hash2-originated-cokolwiek” - może być lepiej, jeśli dodasz numer do klawisza, aby stał się kluczem1, kluczem2 i tak dalej, dopóki nie naciśniesz taki, który nie jest jeszcze w hash1. Poza tym nie robiłem żadnego rubinu przez kilka miesięcy, więc prawdopodobnie nie jest to poprawne składniowo, ale powinieneś być w stanie zrozumieć sedno.

Alternatywnie ponownie zdefiniuj wartość klucza jako tablicę, tak aby hash1 [klucz] zwracał oryginalną wartość z hash1 i wartość z hash2. Zależy od tego, jaki naprawdę ma być wynik.

glenatron
źródło
A jeśli nie zachowasz obu kluczy, ale zsumujesz wartości tego samego klucza?
Tom KC Chiu
1
@ TomK.C.Chiu To w dużej mierze zależałoby od okoliczności, których nie możemy ocenić na podstawie pytania - a co, jeśli wartości w hash1 są ciągami, a hash2 są liczbami całkowitymi? W niektórych przypadkach może to być opłacalna opcja, ale częściej powodowałaby problemy - sugestia użycia list dla wartości działa dość jasno.
glenatron
0

Tutaj możesz połączyć swoje 2 hash przez 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
źródło
0

Jeśli chcesz scalić dwa skróty optionsi defaultsnie nadpisywać docelowego skrótu, możesz sprawdzić, selectczy klucz jest już obecny w docelowym skrócie. Oto czyste rozwiązanie Ruby bez 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}

Lub jeśli klucz jest obecny, ale zawiera nili chcesz go nadpisać:

options.merge!(defaults.select{ |k,_| options[k].nil? })
chrześcijanin
źródło