Comment puis-je fusionner deux hachages sans clés en double écrasées dans Ruby?

141

Existe-t-il un moyen simple ou élégant de fusionner deux hachages sans écraser les clés en double?

Autrement dit, si la clé est présente dans le hachage d'origine, je ne veux pas changer sa valeur.

Claudio Acciaresi
la source
Voulez-vous vraiment dire des tableaux (par exemple: ['a', 'b', 'c']) ou des hachages (par exemple: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner
Désolé, je parlais de hashes :)
Claudio Acciaresi

Réponses:

233

Si vous avez deux hash, optionset defaults, et vous souhaitez fusionner defaultsen optionssans écraser les clés existantes, ce que vous voulez vraiment faire est l'inverse: fusion optionsen defaults:

options = defaults.merge(options)

Ou, si vous utilisez Rails, vous pouvez faire:

options.reverse_merge!(defaults)
Alex Reisner
la source
Tout à fait d'accord, merci beaucoup pour reverse_merge! méthode ne le savait pas :)
Claudio Acciaresi
Pourquoi les parenthèses sont-elles nécessaires ici? Vous ne pouvez pas simplement faire les options default.merge, il apparaît.
Donato
1
Ils sont obsolètes en reverse_merge!raison de problèmes de sécurité dans les rails 5.1
Mirv - Matt
1
@ Mirv-Matt - Je ne vois pas d'avis de dépréciation. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij
17

Il existe un moyen dans la bibliothèque Ruby standard de fusionner les hachages sans écraser les valeurs existantes ni réattribuer le hachage.

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

Si votre problème est que le hachage d'origine et le second peuvent tous deux avoir des clés en double et que vous ne voulez pas écraser dans les deux sens, vous devrez peut-être opter pour une simple fusion manuelle avec une sorte de vérification et de gestion des collisions:

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

De toute évidence, cela est très rudimentaire et suppose que hash1 n'a pas de clé appelée "hash2-originated-what" - vous feriez peut-être mieux d'ajouter simplement un numéro à la clé pour qu'elle devienne key1, key2 et ainsi de suite jusqu'à ce que vous appuyiez sur celui qui n'est pas déjà dans hash1. De plus, je n'ai pas fait de rubis depuis quelques mois, donc ce n'est probablement pas syntaxiquement correct, mais vous devriez être en mesure de comprendre l'essentiel.

Vous pouvez également redéfinir la valeur de la clé sous forme de tableau afin que hash1 [key] renvoie la valeur d'origine de hash1 et la valeur de hash2. Cela dépend de ce que vous voulez vraiment que votre résultat soit.

glénatron
la source
Que diriez-vous si vous ne gardez pas les deux clés, mais en additionnant les valeurs de la même clé?
Tom KC Chiu
1
@ TomK.C.Chiu Cela dépendrait beaucoup des circonstances que nous ne pouvons pas juger à partir de la question - et si les valeurs de hash1 sont des chaînes et hash2 sont des entiers? Dans certains cas, cela pourrait être une option viable, mais le plus souvent, cela poserait des problèmes - la suggestion d'utiliser des listes pour les valeurs fonctionne assez clairement.
glenatron
0

Ici, vous pouvez fusionner vos 2 hachages par 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
la source
0

Si vous souhaitez fusionner les deux hachages optionset defaultssans écraser le hachage de destination, vous pouvez vérifier avec selectsi la clé est déjà présente dans le hachage de destination. Voici la solution pure Ruby sans 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}

Ou si la clé est présente, mais contient nilet que vous souhaitez l'écraser:

options.merge!(defaults.select{ |k,_| options[k].nil? })
Christian
la source