Come posso unire due hash senza le chiavi duplicate sovrascritte in Ruby?


141

Esiste un modo semplice o elegante per unire due hash senza sovrascrivere chiavi duplicate?

Cioè, se la chiave è presente nell'hash originale non voglio cambiarne il valore.


Intendi davvero array (ad es. ['A', 'b', 'c']) o hash (ad es. {'A' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner,

Scusa, stavo parlando di hash :)
Claudio Acciaresi il

Risposte:


233

Se si dispone di due hash, optionse defaults, e si desidera unire defaultsin optionssenza sovrascrivere le chiavi esistenti, ciò che si vuole veramente fare è il contrario: si fondono optionsin defaults:

options = defaults.merge(options)

Oppure, se stai usando Rails puoi fare:

options.reverse_merge!(defaults)

Totalmente d'accordo, grazie mille per reverse_merge! il metodo non lo sapeva :)
Claudio Acciaresi il

Perché sono necessarie le parentesi qui? Non puoi semplicemente fare le opzioni default.merge che appare.
Donato,

1
Stanno deprecando a reverse_merge!causa di problemi di sicurezza nelle rotaie 5.1
Mirv - Matt

1
@ Mirv-Matt - Non vedo un avviso di ammortamento. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij

17

C'è un modo nella libreria standard di Ruby di unire gli hash senza sovrascrivere i valori esistenti o riassegnare l'hash.

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

3

Se il tuo problema è che l'hash originale e la seconda potrebbero avere chiavi duplicate e non vuoi sovrascrivere in nessuna delle due direzioni, potresti dover eseguire una semplice fusione manuale con una sorta di controllo e gestione delle collisioni:

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

Ovviamente, questo è molto rudimentale e presuppone che l'hash1 non abbia chiavi chiamate "hash2-origated-qualunque" - potresti stare meglio semplicemente aggiungendo un numero alla chiave in modo che diventi key1, key2 e così via fino a quando non premi uno che non è già in hash1. Inoltre, non ho fatto alcun rubino per alcuni mesi, quindi probabilmente non è sintatticamente corretto, ma dovresti essere in grado di ottenere l'essenza.

In alternativa, ridefinire il valore della chiave come un array in modo che hash1 [chiave] restituisca il valore originale da hash1 e il valore da hash2. Dipende da cosa vuoi che il tuo risultato sia davvero.


Che ne dici se non mantenere entrambe le chiavi, ma sommare i valori della stessa chiave?
Tom KC Chiu,

1
@ TomK.C.Chiu Dipenderebbe molto dalle circostanze che non possiamo giudicare dalla domanda: cosa succede se i valori in hash1 sono stringhe e hash2 sono numeri interi? Per alcuni casi potrebbe essere un'opzione praticabile, ma più spesso potrebbe causare problemi: il suggerimento di utilizzare gli elenchi per i valori è abbastanza chiaro.
glenatron,

0

Qui puoi unire i tuoi 2 hash di 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

Se vuoi unire i due hash optionse defaultssenza sovrascrivere l'hash di destinazione, puoi verificare selectse la chiave è già presente nell'hash di destinazione. Ecco la pura soluzione Ruby senza 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}

O se la chiave è presente, ma contiene nile si desidera sovrascriverla:

options.merge!(defaults.select{ |k,_| options[k].nil? })
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.