Der beste Weg, Strings Symbole in Hash zu konvertieren
Frage
Was ist der (schnellst / sauberste / einfach) Weg, um alle Schlüssel in einem Hash von Strings Symbolen in Ruby zu konvertieren?
Dies wäre praktisch, wenn YAML Parsen.
my_hash = YAML.load_file('yml')
Ich möchte verwenden können:
my_hash[:key]
Statt:
my_hash['key']
Lösung
Wenn Sie einen Einzeiler,
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
wird der Hash in eine neue mit den Tasten kopieren symbolisiert.
Andere Tipps
Hier ist eine bessere Methode, wenn Sie Rails verwenden:
params. symbolize_keys
Das Ende.
Wenn Sie nicht nur ihren Code Abzocke (es ist auch in 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 werden.
require 'yaml' require 'pp' yaml_str = " connections: - host: host1.example.com port: 10000 - host: host2.example.com port: 20000 " yaml_sym = " :connections: - :host: host1.example.com :port: 10000 - :host: host2.example.com :port: 20000 " pp yaml_str = YAML.load(yaml_str) puts yaml_str.keys.first.class pp yaml_sym = YAML.load(yaml_sym) puts yaml_sym.keys.first.class
Ausgabe:
# /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb {"connections"=> [{"port"=>10000, "host"=>"host1.example.com"}, {"port"=>20000, "host"=>"host2.example.com"}]} String {:connections=> [{:port=>10000, :host=>"host1.example.com"}, {:port=>20000, :host=>"host2.example.com"}]} Symbol
Noch kurz und bündig:
Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
Wenn Sie Rails verwenden, es ist viel einfacher - Sie HashWithIndifferentAccess verwenden können und die Schlüssel zugreifen sowohl als String und als Symbole:
my_hash.with_indifferent_access
siehe auch:
http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html
Sie können auch die awesome „Facetten von Ruby“ Gem verwenden, die eine Menge Erweiterungen Ruby-Entwicklung und Standard Library-Klassen enthält.
require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=> {:some=>"thing", :foo=>"bar}
Da Ruby 2.5.0
Sie verwenden können, Hash#transform_keys
oder Hash#transform_keys!
.
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
http://api.rubyonrails.org/classes/Hash. html # Methode-i-symbolize_keys
hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
Hier ist ein Weg, um tief ein Objekt symbolisiert
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
params.symbolize_keys
wird auch funktionieren. Diese Methode wendet Hash-Schlüssel in Symbole und gibt einen neuen Hash.
Eine Änderung @igorsales beantworten
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
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
Wandelt an:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
So viele Antworten hier, aber die eine Methode Schienen Funktion ist hash.symbolize_keys
Das ist mein Motto für verschachteltes 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
Bei der Grund müssen Sie dies zu tun ist, weil Ihre Daten ursprünglich von JSON kam, Sie gerade vorbei in der :symbolize_names
Option jede dieser Parsing überspringen könnte JSON auf die Einnahme.
Keine Rails erforderlich und arbeiten mit Rubin> 1.9
JSON.parse(my_json, :symbolize_names => true)
Sie könnten faul sein, und wickeln Sie es in einem lambda
:
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 aber nur funktionieren, für aus dem Hash Lesen -. Nicht schreiben
Um das zu tun, könnten Sie Hash#merge
verwenden
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
Der init-Block wird die Schlüssel einmal auf Nachfrage konvertieren, obwohl, wenn Sie den Wert für die String-Version des Schlüssels aktualisieren, nachdem das Symbol Version zugreifen, wird das Symbol Version nicht aktualisiert werden.
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}
Sie könnte auch die init-Block den Hash nicht aktualisieren, die Sie von dieser Art von Fehler schützen würde, aber Sie würden immer noch auf der gegenüberliegenden anfällig sein - das Symbol Version zu aktualisieren würde die String-Version nicht aktualisiert werden:
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}
So ist die Sache vorsichtig zu sein, mit diesen zwischen den beiden Schlüsselformen wechseln. Stick mit einem.
wäre so etwas wie die folgende Arbeit?
new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }
Es wird der Hash kopieren, aber Sie werden nicht kümmern uns um, dass die meiste Zeit. Wahrscheinlich gibt es eine Möglichkeit, es zu tun, ohne alle Daten zu kopieren.
ein kürzerer Einzeiler FWIW:
my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
Wie wäre es damit:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
Dies ist für Leute, die mruby
verwendet und haben keine symbolize_keys
Methode definiert:
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:
- symbolisiert nur Schlüssel, die
String
sind
- , wenn eine Zeichenfolge symbolisieren bedeutet, einige Informationen zu verlieren (Teil des Hash überschreiben) erhöhen, um eine
RuntimeError
- symbolisieren auch rekursiv Hashes enthalten
- Rückkehr der symbolisierten Hash
- arbeitet an Ort und Stelle!
Das Array wollen wir ändern.
Strings = [ "HTML", "CSS", "JavaScript", "Python", "Ruby"]
Erstellen Sie eine neue Variable als leeres Array so können wir ".push" die Symbole in.
Symbole = []
Hier ist, wo wir ein Verfahren mit einem Block definieren.
strings.each {| x | symbols.push (x.intern)}
Ende des Codes.
Das ist also wahrscheinlich die einfachste Art und Weise Strings Symbole in Ihrem Array (s) in Ruby zu konvertieren. Machen Sie ein Array von Strings dann eine neue Variable machen und die Variable auf ein leeres Array gesetzt. Dann wählen die jedes Element in dem ersten Array mit dem Sie „.each“ -Methode erstellt. Dann mit einem Blockcode auf „.push“ alle Elemente in Ihrem neuen Array und verwenden „.intern oder .to_sym“ alle Elemente Symbole zu konvertieren.
Die Symbole sind schneller, weil sie mehr Speicher in Ihrem Code speichern und Sie können sie nur einmal verwenden. Symbole werden am häufigsten für Schlüssel in Hash verwendet, die groß ist. Ich bin das nicht die beste Rubin Programmierer, aber diese Form des Code hat mir geholfen, ein lot.If jemand weiß, einen besseren Weg, bitte teilen und Sie können diese Methode für Hash verwenden!
Wenn Sie Vanille Rubin Lösung gefällt und wie mir den Zugang nicht hier ActiveSupport
haben, ist tief symbolize Lösung (sehr ähnlich wie die 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
Ab Psych 3.0 können Sie fügen Sie den symbolize_names: Option
Psych.load("---\n foo: bar")
# => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true)
# => {:foo=>"bar"}
. Hinweis: wenn Sie eine niedrigere Psych Version haben als 3,0 symbolize_names:
wird stillschweigend ignoriert
Meine Ubuntu 18.04 enthält es aus der Box mit Ruby 2.5.1p57
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}
Dies ist nicht gerade ein Einzeiler, aber es stellt sich alle String-Schlüssel in Symbole, auch die verschachtelten Einsen:
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
Ich mag diese Einzeiler, wenn ich nicht Rails verwenden, denn dann muss ich nicht einen zweiten Hash machen und zwei Sätze von Daten halten, während ich es bin Verarbeitung:
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 # gibt den Wert des gelöschten Schlüssel löschen
Facets' Hash # deep_rekey ist auch eine gute Option, vor allem:
- Wenn Sie für anderen Zucker von Facetten in Ihrem Projekt verwenden finden,
- , wenn Sie die Lesbarkeit des Codes über kryptische Einzeiler bevorzugen.
Beispiel:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
In Ruby finde ich das die einfache und leicht verständliche Art und Weise sein String-Schlüssel in Hashes Symbole zu drehen:
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
Für jeden Schlüssel in der Hash wir sie löschen nennen, die es aus dem Hash entfernt (auch löschen gibt den Wert im Zusammenhang mit der Taste , die gelöscht wurde), und wir setzen sofort diese gleich der symbolisierten Taste.
ähnlich wie bei bisherigen Lösungen, aber ein bisschen anders geschrieben.
- Dies ermöglicht einen Hash, die verschachtelten und / oder hat Arrays.
- Get Umwandlung von Schlüsseln in einen String als Bonus.
-
-Code wird der Hash nicht mutiert worden übergeben.
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