Was ist der (schnellste/sauberste/einfachste) Weg, um alle Schlüssel in einem Hash von Strings in Symbole in Ruby zu konvertieren?
Dies wäre praktisch, wenn Sie YAML analysieren.
my_hash = YAML.load_file('yml')
Ich möchte in der Lage sein zu verwenden:
my_hash[:key]
Eher, als:
my_hash['key']
Wenn Sie einen One-Liner wollen,
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
kopiert den Hash in einen neuen mit den symbolisierten Schlüsseln.
Hier ist eine bessere Methode, wenn Sie Rails verwenden:
params. symbolize_keys
Das Ende.
Wenn Sie nicht sind, reißen Sie einfach ihren Code ab (auch im Link):
myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
Für den speziellen Fall von YAML in Ruby, wenn die Schlüssel mit ':
' beginnen, werden sie automatisch als Symbole interniert.
erfordern 'yaml' erfordern 'pp' yaml_str = " Verbindungen: - Host: Host1.example.com Port: 10000 - Host: Host2. example.com Port: 20000 " yaml_sym =" : Verbindungen: -: Host: Host1.example.com : Port: 10000 -: Host : Host2.example.com : Port: 20000 " Pp yaml_str = YAML.load (yaml_str) Setzt yaml_str.keys.first.class Pp yaml_sym = yaml_sym) setzt yaml_sym.keys.first.class
Ausgabe:
# /opt/Ruby-1.8.6-p287/bin/Ruby ~/test.rb {"verbindungen" => [{"port" => 10000, "Host" => "Host1.example.com"}, {"port" => 20000, "Host" => "Host2.example.com"}]} String {: verbindungen => [{: port => 10000,: Host => "Host1.example.com"}, {: port => 20000,: Host => "Host2.example.com"}]} Symbol
Noch knapper:
Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
wenn Sie Rails verwenden, ist es viel einfacher - Sie können einen HashWithIndifferentAccess verwenden und auf die Schlüssel sowohl als String als auch als Symbole zugreifen:
my_hash.with_indifferent_access
siehe auch:
http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html
Oder Sie können den fantastischen "Facets of Ruby" Gem verwenden, der viele Erweiterungen der Klassen Ruby Core und Standard Library enthält.
require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=> {:some=>"thing", :foo=>"bar}
http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys
hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
Seit Ruby 2.5.0
können Sie Hash#transform_keys
oder Hash#transform_keys!
verwenden.
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
Hier ist ein Weg, um ein Objekt tief zu symbolisieren
def symbolize(obj)
return obj.inject({}){|memo,(k,v)| memo[k.to_sym] = symbolize(v); memo} if obj.is_a? Hash
return obj.inject([]){|memo,v | memo << symbolize(v); memo} if obj.is_a? Array
return obj
end
Ich mag den Mash Edelstein wirklich.
sie können mash['key']
oder mash[:key]
oder mash.key
tun
params.symbolize_keys
wird auch funktionieren. Diese Methode wandelt Hash-Schlüssel in Symbole und gibt einen neuen Hash zurück.
Eine Änderung an der Antwort von @igorsales
class Object
def deep_symbolize_keys
return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
return self.inject([]){|memo,v | memo << v.deep_symbolize_keys; memo} if self.is_a? Array
return self
end
end
Wenn Sie json verwenden und es als Hash verwenden möchten, können Sie dies in Core Ruby tun:
json_obj = JSON.parse(json_str, symbolize_names: true)
symbolize_names: Wenn auf true gesetzt, werden Symbole für die Namen (Schlüssel) in einem JSON-Objekt zurückgegeben. Andernfalls werden Zeichenfolgen zurückgegeben. Zeichenfolgen sind die Standardeinstellung.
Dies ist mein einziger Liner für verschachtelte Hashes
def symbolize_keys(hash)
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
So viele Antworten hier, aber die eine Methode der Rails-Funktion ist hash.symbolize_keys
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
Konvertiert in:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
Für den Fall, dass reason erforderlich ist, weil Ihre Daten ursprünglich von JSON stammen, können Sie diese Analyse überspringen, indem Sie beim Einlesen von JSON die Option :symbolize_names
übergeben.
Keine Rails erforderlich und funktioniert mit Ruby> 1.9
JSON.parse(my_json, :symbolize_names => true)
Sie könnten faul sein und es in eine lambda
einwickeln:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
Dies würde jedoch nur zum Lesen aus dem Hash funktionieren - nicht zum Schreiben.
Dazu können Sie Hash#merge
verwenden.
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
Der Init-Block konvertiert die Schlüssel einmalig auf Anforderung. Wenn Sie jedoch nach dem Zugriff auf die Symbolversion den Wert für die String-Version des Schlüssels aktualisieren, wird die Symbolversion nicht aktualisiert.
irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a] # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a] # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}
Der Init-Block könnte den Hash auch nicht aktualisieren, was Sie vor solchen Fehlern schützen würde, aber Sie wären immer noch anfällig für das Gegenteil. Wenn Sie die Symbolversion aktualisieren, wird die String-Version nicht aktualisiert:
irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}
Das Wichtigste dabei ist das Umschalten zwischen den beiden Schlüsselformen. Bleib bei einem.
ein kürzerer Einliner fwiw:
my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
Dies ist für Personen, die mruby
verwenden und keine symbolize_keys
-Methode definiert haben:
class Hash
def symbolize_keys!
self.keys.each do |k|
if self[k].is_a? Hash
self[k].symbolize_keys!
end
if k.is_a? String
raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
self[k.to_sym] = self[k]
self.delete(k)
end
end
return self
end
end
Die Methode:
String
sindRuntimeError
.Wie wäre es damit:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
Würde so etwas wie das folgende funktionieren?
new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }
Es wird den Hash kopieren, aber das interessiert Sie meistens nicht. Es gibt wahrscheinlich eine Möglichkeit, dies zu tun, ohne alle Daten zu kopieren.
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symbole = []
strings.each {| x | symbols.Push (x.intern)}
Dies ist wahrscheinlich der einfachste Weg, Zeichenfolgen in Symbole in Ihren Arrays in Ruby zu konvertieren. Erstellen Sie ein String-Array, erstellen Sie eine neue Variable und setzen Sie die Variable auf ein leeres Array. Wählen Sie dann jedes Element im ersten Array aus, das Sie mit der Methode ".each" erstellt haben. Verwenden Sie dann einen Blockcode, um alle Elemente in Ihrem neuen Array zu ".Push" und verwenden Sie ".intern" oder ".to_sym", um alle Elemente in Symbole zu konvertieren.
Symbole sind schneller, da sie mehr Speicherplatz in Ihrem Code sparen und Sie nur einmal verwenden können. Symbole werden am häufigsten für Schlüssel in Hash verwendet, was großartig ist. Ich bin nicht der beste Ruby-Programmierer, aber diese Form des Codes hat mir sehr geholfen. Wenn jemand einen besseren Weg kennt, teilen Sie ihn bitte und Sie können diese Methode auch für Hash verwenden!
Wenn Sie eine Vanilla Ruby-Lösung möchten und da ich keinen Zugriff auf ActiveSupport
habe, wird hier eine tiefe Lösung symbolisiert (sehr ähnlich der vorherigen)
def deep_convert(element)
return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
element
end
In Ruby finde ich, dass dies die einfachste und einfachste Methode ist, Zeichenfolgenschlüssel in Hashes in Symbole umzuwandeln:
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
Für jeden Schlüssel im Hash rufen wir delete auf, der ihn aus dem Hash entfernt (auch delete gibt den mit dem Schlüssel verknüpften Wert zurück der gelöscht wurde) und setzen diesen Wert sofort dem symbolisierten Schlüssel.
Ich mag diesen One-Liner, wenn ich Rails nicht verwende, weil ich dann keinen zweiten Hash machen und zwei Datensätze halten muss, während ich ihn verarbeite:
my_hash = { "a" => 1, "b" => "string", "c" => true }
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }
my_hash
=> {:a=>1, :b=>"string", :c=>true}
Hash # delete gibt den Wert des gelöschten Schlüssels zurück
Dies ist nicht gerade ein Einzeiler, aber es werden alle Zeichenkettenschlüssel in Symbole umgewandelt, auch die verschachtelten:
def recursive_symbolize_keys(my_hash)
case my_hash
when Hash
Hash[
my_hash.map do |key, value|
[ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
end
]
when Enumerable
my_hash.map { |value| recursive_symbolize_keys(value) }
else
my_hash
end
end
Ähnlich wie bei früheren Lösungen, jedoch etwas anders geschrieben.
Code verändert den übergebenen Hash nicht.
module HashUtils
def symbolize_keys(hash)
transformer_function = ->(key) { key.to_sym }
transform_keys(hash, transformer_function)
end
def stringify_keys(hash)
transformer_function = ->(key) { key.to_s }
transform_keys(hash, transformer_function)
end
def transform_keys(obj, transformer_function)
case obj
when Array
obj.map{|value| transform_keys(value, transformer_function)}
when Hash
obj.each_with_object({}) do |(key, value), hash|
hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
end
else
obj
end
end
end
Ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
=> {"aaa"=>1, "bbb"=>2}
Ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
=> {:aaa=>1, :bbb=>2}
Ab Psych 3.0 können Sie die Option symbolize_names: hinzufügen
Psych.load("---\n foo: bar")
# => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true)
# => {:foo=>"bar"}
Hinweis: Wenn Sie eine niedrigere Psych-Version als 3.0 haben, wird symbolize_names:
still ignoriert.
Mein Ubuntu 18.04 enthält es mit Ruby 2.5.1p57
Facets 'Hash # deep_rekey ist auch eine gute Option, vor allem:
Probe:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey