Wie kann ich zwei Hashes zusammenführen, ohne doppelte Schlüssel in Ruby zu überschreiben?

140

Gibt es eine einfache oder elegante Möglichkeit, zwei Hashes zusammenzuführen, ohne doppelte Schlüssel zu überschreiben?

Das heißt, wenn der Schlüssel im ursprünglichen Hash vorhanden ist, möchte ich seinen Wert nicht ändern.

Claudio Acciaresi
quelle
Meinen Sie wirklich Arrays (zB: ['a', 'b', 'c']) oder Hashes (zB: {'a' => 1, 'b' => 2, 'c' => 3}) ?
Alex Reisner
Entschuldigung, ich habe über Hashes gesprochen :)
Claudio Acciaresi

Antworten:

232

Wenn Sie zwei Hashes haben, optionsund defaults, und Sie zusammenführen möchten defaultsin optionsohne vorhandene Schlüssel überschreiben, was Sie wirklich wollen , zu tun , ist das Gegenteil: merge optionsin defaults:

options = defaults.merge(options)

Wenn Sie Rails verwenden, können Sie Folgendes tun:

options.reverse_merge!(defaults)
Alex Reisner
quelle
Stimme voll und ganz zu, vielen Dank für reverse_merge! Methode wusste es nicht :)
Claudio Acciaresi
WARUM werden hier die Klammern benötigt? Sie können nicht einfach default.merge-Optionen ausführen, die angezeigt werden.
Donato
1
Sie sind reverse_merge!aufgrund von Sicherheitsproblemen in Schienen 5.1
Mirv - Matt
@ Mirv-Matt - Ich sehe keinen Abschreibungsbescheid. apidock.com/rails/v6.0.0/Hash/reverse_merge%21
Kshitij
17

In der Standard-Ruby-Bibliothek gibt es eine Möglichkeit, Hashes zusammenzuführen, ohne vorhandene Werte zu überschreiben oder den Hash neu zuzuweisen.

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

Wenn Ihr Problem darin besteht, dass der ursprüngliche Hash und der zweite Hash möglicherweise doppelte Schlüssel haben und Sie nicht in beide Richtungen überschreiben möchten, müssen Sie möglicherweise eine einfache manuelle Zusammenführung mit einer Art Kollisionsprüfung und -behandlung durchführen:

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

Dies ist natürlich sehr rudimentär und setzt voraus, dass Hash1 keine Schlüssel mit der Bezeichnung "Hash2-Originated-Whatever" hat. Möglicherweise ist es besser, nur eine Nummer zum Schlüssel hinzuzufügen, damit er zu Key1, Key2 usw. wird, bis Sie auf drücken eine, die noch nicht in hash1 enthalten ist. Außerdem habe ich seit einigen Monaten keinen Rubin mehr gemacht, daher ist das wahrscheinlich nicht syntaktisch korrekt, aber Sie sollten in der Lage sein, das Wesentliche zu verstehen.

Alternativ können Sie den Wert des Schlüssels als Array neu definieren, sodass Hash1 [Schlüssel] den ursprünglichen Wert von Hash1 und den Wert von Hash2 zurückgibt. Kommt darauf an, was dein Ergebnis wirklich sein soll.

Glenatron
quelle
Wie wäre es, wenn Sie nicht beide Schlüssel behalten, sondern die Werte desselben Schlüssels addieren?
Tom KC Chiu
1
@ TomK.C.Chiu Das würde sehr stark von Umständen abhängen, die wir anhand der Frage nicht beurteilen können - was ist, wenn die Werte in Hash1 Zeichenfolgen und Hash2 Ganzzahlen sind? In einigen Fällen ist dies möglicherweise eine praktikable Option, die jedoch häufiger zu Problemen führt. Der Vorschlag, Listen für Werte zu verwenden, umgeht dies recht sauber.
Glenatron
0

Hier können Sie Ihre 2 Hashs zusammenführen 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
quelle
0

Wenn Sie die beiden Hashes zusammenführen möchten optionsund defaultsohne Überschreiben der Ziel Hash, können Sie mit überprüfen , selectob der Schlüssel bereits in der Ziel - Hash. Hier ist die reine Ruby-Lösung ohne 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}

Oder wenn der Schlüssel vorhanden ist, aber enthält nilund Sie ihn überschreiben möchten:

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