So ersetzen Sie einen Hash-Schlüssel durch einen anderen Schlüssel

191

Ich habe einen Zustand, in dem ich einen Hash bekomme

  hash = {"_id"=>"4de7140772f8be03da000018", .....}

und ich will diesen Hash als

  hash = {"id"=>"4de7140772f8be03da000018", ......}

PS : Ich weiß nicht, was die Schlüssel im Hash sind, sie sind zufällig und haben ein "_" -Präfix für jeden Schlüssel und ich möchte keine Unterstriche

Manish Das
quelle
Dies könnte Ihnen helfen: stackoverflow.com/questions/4044451/…
korrodiert
+1 für nützliche Frage
ashisrai_
@ a5his: Ich bin froh, dass es hilfreich war :)
Manish Das

Antworten:

710
hash[:new_key] = hash.delete :old_key
gayavat
quelle
8
Hat mir ein paar LOC gerettet, ich liebe es!
Nicohvi
10
Ich mag oft keinen "intelligenten" Ruby-Code, weil es einige Zeit dauert, um zu sagen, was er wirklich tut. Ihre Lösung ist dagegen einfach und beschreibend.
Lucas
3
Dies sollte in der Tat die akzeptierte Antwort sein! Einfach, sauber und direkt auf den Punkt!
GigaBass
1
Diese Antwort ist elegant, beantwortet aber die Frage nicht wirklich. Der Beitrag besagt, dass die Schlüssel, die ersetzt werden müssen, unbekannt sind ... Wir wissen nur, dass sie mit einem Unterstrich beginnen, wir wissen nicht, was die Schlüssel tatsächlich sind.
Dsel
2
Dadurch wird ein neues Schlüssel / Wert-Paar erstellt, in dem Sie den neuen Schlüssel angeben und den Wert aus der hash.delete :old_keyRückgabe abrufen. Beim Löschen wird der alte Schlüssel verwendet. WOW, ich möchte, dass es irgendwo tätowiert wird :-D Danke
Bart C
136

Schienen Hash hat Standardmethode dafür:

hash.transform_keys{ |key| key.to_s.upcase }

http://api.rubyonrails.org/classes/Hash.html#method-i-transform_keys

UPD: Ruby 2.5-Methode

gayavat
quelle
4
Es ist die Rails-Methode, nicht der Standard. Gute Antwort.
user2422869
1
Außerdem kann diese Methode nicht rekursiv mit Hash-Schlüsseln arbeiten.
Sergio Belevskij
5
deep_transform_keys kann dafür verwendet werden :) apidock.com/rails/v4.2.1/Hash/deep_transform_keys
gayavat
1
Zu guter Letzt! Genau das habe ich gesucht!
TiSer
4
Dies ist ein Standardteil der Sprache ab Ruby 2.5: docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
David Grayson
39

Wenn alle Schlüssel Zeichenfolgen sind und alle das Unterstrichpräfix haben, können Sie den Hash wie folgt patchen:

h.keys.each { |k| h[k[1, k.length - 1]] = h[k]; h.delete(k) }

Das k[1, k.length - 1]Bit erfasst alle kaußer dem ersten Zeichen. Wenn Sie eine Kopie wünschen, dann:

new_h = Hash[h.map { |k, v| [k[1, k.length - 1], v] }]

Oder

new_h = h.inject({ }) { |x, (k,v)| x[k[1, k.length - 1]] = v; x }

Sie können auch verwenden, subwenn Ihnen die k[]Notation zum Extrahieren eines Teilstrings nicht gefällt :

h.keys.each { |k| h[k.sub(/\A_/, '')] = h[k]; h.delete(k) }
Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

Und wenn nur einige der Schlüssel das Unterstrichpräfix haben:

h.keys.each do |k|
  if(k[0,1] == '_')
    h[k[1, k.length - 1]] = h[k]
    h.delete(k)
  end
end

Ähnliche Änderungen können an allen anderen oben genannten Varianten vorgenommen werden, außer diesen beiden:

Hash[h.map { |k, v| [k.sub(/\A_/, ''), v] }]
h.inject({ }) { |x, (k,v)| x[k.sub(/\A_/, '')] = v; x }

sollte mit Schlüsseln in Ordnung sein, die keine Unterstrichpräfixe ohne zusätzliche Änderungen haben.

mu ist zu kurz
quelle
Ihre Antwort hat funktioniert, aber nach der Station habe ich nur wenige solche Hash gefunden
Manish Das
3
{"_id" => "4de7140772f8be03da000018", "_type" => "WorkStation", "created_at" => "2011-06-02T10: 24: 35 + 05: 45", "input_header_ids" => [], "line_id" "=>" 4de7140472f8be03da000017 "," updated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das
2
{"id" => "4de7140772f8be03da000018", "type" => "WorkStation", "reated_at" => "2011-06-02T10: 24: 35 + 05: 45", "nput_header_ids" => [], "ine_id "=>" 4de7140472f8be03da000017 "," pdated_at "=>" 2011-06-02T10: 24: 35 + 05: 45 "}
Manish Das
@ Manish: Ich habe gesagt "Wenn alle Schlüssel Zeichenfolgen sind und alle das Unterstrichpräfix haben". Ich habe einen Beispielansatz für Ihre "Schlüssel ohne Unterstrichpräfixe" in ein Update aufgenommen.
Mu ist zu kurz
2
@Manish: "k" steht für "key", "v" steht für "value", "x" steht für "Ich weiß nicht, wie ich es nennen soll, aber ich wurde als Mathematiker ausgebildet, also nenne ich es x".
Mu ist zu kurz
14

du kannst tun

hash.inject({}){|option, (k,v) | option["id"] = v if k == "_id"; option}

Dies sollte für Ihren Fall funktionieren!

Sadiksha Gautam
quelle
11

Wenn wir einen bestimmten Schlüssel in Hash umbenennen möchten, können wir dies wie folgt tun:
Angenommen, mein Hash ist my_hash = {'test' => 'ruby hash demo'}
Jetzt möchte ich 'Test' durch 'Nachricht' ersetzen, dann:
my_hash['message'] = my_hash.delete('test')

Swapnil Chincholkar
quelle
Wie ist Ihre Antwort dann Lösung für mein Problem? Wenn Sie dies für hilfreich hielten, hätten Sie dies im Kommentar unter der Frage hinzufügen können. Meine Frage war nicht, einen Schlüssel durch einen anderen Schlüssel zu ersetzen. Die von Ihnen angegebene Lösung ist eine sehr einfache Hash-Eigenschaft. In meinem Fall ist es nicht hash[:new_key] = has[:old_key]:, sondern: hash[:dynamic_key] = hash[:_dynamic_key]Es war eine klare Frage zu Regex und kein einfacher Hash-Ersatz.
Manish Das
2
Ich bin über eine Google-Suche dazu gekommen und wollte die Antwort von @ Swapnil. Danke
toobulkeh
10
h.inject({}) { |m, (k,v)| m[k.sub(/^_/,'')] = v; m }
DigitalRoss
quelle
4
Ich finde es gut, dass Sie versucht haben, die Unterstriche mit einem regulären Ausdruck herauszufiltern, aber Sie sollten sich darüber im Klaren sein, dass / ^ / in Ruby im Gegensatz zu Javascript und anderen "Anfang der Zeichenfolge ODER LINIE" und / $ / "Ende" bedeutet Zeichenfolge ODER LINIE '. In diesem Fall ist es unwahrscheinlich, dass die Schlüssel Zeilenumbrüche enthalten. Sie sollten sich jedoch bewusst sein, dass die Verwendung dieser beiden Operatoren in Ruby nicht nur fehleranfällig, sondern auch sehr gefährlich ist, wenn sie bei Validierungen gegen Injektionen falsch verwendet werden. Sehen Sie hier für eine Erklärung. Ich hoffe, es macht Ihnen nichts aus, das Bewusstsein zu verbreiten.
Jorn van de Beek
2
hash.each {|k,v| hash.delete(k) && hash[k[1..-1]]=v if k[0,1] == '_'}
Maerics
quelle
1

Ich habe übertrieben und mir folgendes ausgedacht. Meine Motivation dahinter war, an Hash-Schlüssel anzuhängen, um Bereichskonflikte beim Zusammenführen / Reduzieren von Hashes zu vermeiden.

Beispiele

Hash-Klasse erweitern

Fügt Hash-Instanzen eine Rekey-Methode hinzu.

# Adds additional methods to Hash
class ::Hash
  # Changes the keys on a hash
  # Takes a block that passes the current key
  # Whatever the block returns becomes the new key
  # If a hash is returned for the key it will merge the current hash 
  # with the returned hash from the block. This allows for nested rekeying.
  def rekey
    self.each_with_object({}) do |(key, value), previous|
      new_key = yield(key, value)
      if new_key.is_a?(Hash)
        previous.merge!(new_key)
      else
        previous[new_key] = value
      end
    end
  end
end

Beispiel voranstellen

my_feelings_about_icecreams = {
  vanilla: 'Delicious',
  chocolate: 'Too Chocolatey',
  strawberry: 'It Is Alright...'
}

my_feelings_about_icecreams.rekey { |key| "#{key}_icecream".to_sym }
# => {:vanilla_icecream=>"Delicious", :chocolate_icecream=>"Too Chocolatey", :strawberry_icecream=>"It Is Alright..."}

Trimmbeispiel

{ _id: 1, ___something_: 'what?!' }.rekey do |key|
  trimmed = key.to_s.tr('_', '')
  trimmed.to_sym
end
# => {:id=>1, :something=>"what?!"}

Abflachen und Anhängen eines "Bereichs"

Wenn Sie einen Hash an rekey zurückgeben, wird der Hash zusammengeführt, wodurch Sie Sammlungen reduzieren können. Auf diese Weise können wir unseren Schlüsseln beim Reduzieren eines Hashs einen Bereich hinzufügen, um ein Überschreiben eines Schlüssels beim Zusammenführen zu vermeiden.

people = {
  bob: {
    name: 'Bob',
    toys: [
      { what: 'car', color: 'red' },
      { what: 'ball', color: 'blue' }
    ]
  },
  tom: {
    name: 'Tom',
    toys: [
      { what: 'house', color: 'blue; da ba dee da ba die' },
      { what: 'nerf gun', color: 'metallic' }
    ]
  }
}

people.rekey do |person, person_info|
  person_info.rekey do |key|
    "#{person}_#{key}".to_sym
  end
end

# =>
# {
#   :bob_name=>"Bob",
#   :bob_toys=>[
#     {:what=>"car", :color=>"red"},
#     {:what=>"ball", :color=>"blue"}
#   ],
#   :tom_name=>"Tom",
#   :tom_toys=>[
#     {:what=>"house", :color=>"blue; da ba dee da ba die"},
#     {:what=>"nerf gun", :color=>"metallic"}
#   ]
# }
CTS_AE
quelle
0

Frühere Antworten sind gut genug, können jedoch die Originaldaten aktualisieren. Falls Sie nicht möchten, dass die Originaldaten betroffen sind, können Sie meinen Code ausprobieren.

 newhash=hash.reject{|k| k=='_id'}.merge({id:hash['_id']})

Zuerst wird der Schlüssel '_id' ignoriert und dann mit dem aktualisierten zusammengeführt.

Purushothaman Poovai
quelle