J'ai un hash Ruby qui ressemble à:
{ "id" => "123", "name" => "test" }
Je voudrais le convertir en:
{ :id => "123", :name => "test" }
hash = {"Apple" => "banana", "coconut" => "domino"}
Hash[hash.map{ |k, v| [k.to_sym, v] }]
#=> {:Apple=>"banana", :coconut=>"domino"}
Mettre à jour:
@mu est trop court: vous n'avez pas vu Word "récursif", mais si vous insistez (avec une protection contre un to_sym
inexistant, rappelez-vous simplement que dans Ruby 1.8 1.to_sym == nil
, jouer avec certains types de clé peut être trompeur):
hash = {"a" => {"b" => "c"}, "d" => "e", Object.new => "g"}
s2s =
lambda do |h|
Hash === h ?
Hash[
h.map do |k, v|
[k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]]
end
] : h
end
s2s[hash] #=> {:d=>"e", #<Object:0x100396ee8>=>"g", :a=>{:b=>"c"}}
Si vous vous trouvez dans Rails, alors vous aurez symbolize_keys
:
Renvoie un nouveau hachage avec toutes les clés converties en symboles, à condition qu'elles répondent à
to_sym
.
et symbolize_keys!
qui fait la même chose mais fonctionne sur place. Donc, si vous êtes dans Rails, vous pouvez:
hash.symbolize_keys!
Si vous voulez symboliser de manière récursive les hachages intérieurs, je pense que vous devriez le faire vous-même, mais avec quelque chose comme ceci:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.to_sym
h[ks] = h.delete k
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
Vous voudrez peut-être jouer avec le kind_of? Hash
pour l'adapter à votre situation spécifique; Utiliser respond_to? :keys
pourrait avoir plus de sens. Et si vous voulez autoriser les clés qui ne comprennent pas to_sym
, alors:
def symbolize_keys_deep!(h)
h.keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
h[ks] = h.delete k # Preserve order even when k == ks
symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
end
end
Notez que h[ks] = h.delete k
ne modifie pas le contenu du hachage lorsque k == ks
, mais conserve l'ordre dans lequel vous utilisez Ruby 1.9+. Vous pouvez également utiliser l'approche [(key.to_sym rescue key) || key]
utilisée par Rails dans leur symbolize_keys!
, mais je pense que c'est un abus du système de gestion des exceptions.
Le deuxième symbolize_keys_deep!
devient:
{ 'a' => 'b', 'c' => { 'd' => { 'e' => 'f' }, 'g' => 'h' }, ['i'] => 'j' }
dans ceci:
{ :a => 'b', :c => { :d => { :e => 'f' }, :g => 'h' }, ['i'] => 'j' }
Si vous le vouliez vraiment, vous pouvez corriger l'une ou l'autre version de symbolize_keys_deep!
dans Hash, mais je reste généralement à l'écart de l'application des correctifs, sauf si j'ai de très bonnes raisons de le faire.
Si vous utilisez Rails> = 4, vous pouvez utiliser:
hash.deep_symbolize_keys
hash.deep_symbolize_keys!
ou
hash.deep_stringify_keys
hash.deep_stringify_keys!
voir http://apidock.com/Rails/v4.2.1/Hash/deep_symbolize_keys
Juste au cas où vous analysez JSON, à partir de json docs vous pouvez ajouter l’option pour symboliser les clés lors de l’analyse:
hash = JSON.parse(json_data, symbolize_names: true)
Victor Moroz a fourni une belle réponse pour le cas simple et récurrent, mais il ne traitera pas les hachages imbriqués dans des tableaux imbriqués:
hash = { "a" => [{ "b" => "c" }] }
s2s[hash] #=> {:a=>[{"b"=>"c"}]}
Si vous devez prendre en charge les hachages dans les tableaux au sein des hachages, vous voudrez quelque chose de plus semblable à ceci:
def recursive_symbolize_keys(h)
case h
when Hash
Hash[
h.map do |k, v|
[ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ]
end
]
when Enumerable
h.map { |v| recursive_symbolize_keys(v) }
else
h
end
end
Essaye ça:
hash = {"Apple" => "banana", "coconut" => "domino"}
# => {"Apple"=>"banana", "coconut"=>"domino"}
hash.tap do |h|
h.keys.each { |k| h[k.to_sym] = h.delete(k) }
end
# => {:Apple=>"banana", :coconut=>"domino"}
Cela effectue une itération sur les clés, et pour chacune d’elles, il supprime la clé stringifiée et attribue sa valeur à la clé symbolisée.
À partir de Ruby 2.5, vous pouvez utiliser la méthode transform_key
: https://docs.Ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys
Donc dans votre cas serait:
h = { "id" => "123", "name" => "test" }
h.transform_keys!(&:to_sym) #=> {:id=>"123", :name=>"test"}
Remarque: les mêmes méthodes sont également disponibles sur Ruby on Rails.
Si vous utilisez Rails (ou simplement ActiveSupport):
{ "id" => "123", "name" => "test" }.symbolize_keys
Ruby one-liner qui est plus rapide que la réponse choisie
hash = {"Apple" => "banana", "coconut" => "domino"}
#=> {"Apple"=>"banana", "coconut"=>"domino"}
hash.inject({}){|h,(k,v)| h[k.intern] = v; h}
#=> {:Apple=>"banana", :coconut=>"domino"}
Résultats de référence
n = 100000
Benchmark.bm do |bm|
bm.report { n.times { hash.inject({}){|h,(k,v)| h[k.intern] = v; h} } }
bm.report { n.times { Hash[hash.map{ |k, v| [k.to_sym, v] }] } }
end
# => user system total real
# => 0.100000 0.000000 0.100000 ( 0.107940)
# => 0.120000 0.010000 0.130000 ( 0.137966)
Vous pouvez également étendre la classe de base Hash Ruby en plaçant un fichier /lib/hash.rb:
class Hash
def symbolize_keys_deep!
new_hash = {}
keys.each do |k|
ks = k.respond_to?(:to_sym) ? k.to_sym : k
if values_at(k).first.kind_of? Hash or values_at(k).first.kind_of? Array
new_hash[ks] = values_at(k).first.send(:symbolize_keys_deep!)
else
new_hash[ks] = values_at(k).first
end
end
new_hash
end
end
Si vous voulez vous assurer que les clés d'un hachage encapsulé dans des tableaux à l'intérieur de votre hachage parent sont symbolisées, vous devez également étendre la classe array en créant un fichier "array.rb" avec ce code:
class Array
def symbolize_keys_deep!
new_ar = []
self.each do |value|
new_value = value
if value.is_a? Hash or value.is_a? Array
new_value = value.symbolize_keys_deep!
end
new_ar << new_value
end
new_ar
end
end
Cela permet d'appeler "symbolize_keys_deep!" sur n'importe quelle variable de hachage comme ceci:
myhash.symbolize_keys_deep!
Je suis partial à:
irb
Ruby-1.9.2-p290 :001 > hash = {"Apple" => "banana", "coconut" => "domino"}
{
"Apple" => "banana",
"coconut" => "domino"
}
Ruby-1.9.2-p290 :002 > hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
{
:Apple => "banana",
:coconut => "domino"
}
Cela fonctionne parce que nous parcourons le hachage et en construisons un nouveau à la volée. Ce n'est pas récursif, mais vous pouvez le comprendre en regardant certaines des autres réponses.
hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
Voici mes deux cents,
ma version de symbolize_keys_deep!
utilise l'original symbolize_keys! fourni par Rails et effectue simplement un appel récursif simple pour symboliser les sous-tables.
def symbolize_keys_deep!(h)
h.symbolize_keys!
h.each do |k, v|
symbolize_keys_deep!(v) if v.is_a? Hash
end
end
Facettes 'Hash # rekey est également à mentionner.
Échantillon:
require 'facets/hash/rekey'
{ "id" => "123", "name" => "test" }.deep_rekey
=> {:id=>"123", :name=>"test"}
Il existe également une version récursive:
require 'facets/hash/deep_rekey'
{ "id" => "123", "name" => {"first" => "John", "last" => "Doe" } }.deep_rekey
=> {:id=>"123", :name=>{:first=>"John", :last=>"Doe"}}
def symbolize_keys(hash)
new={}
hash.map do |key,value|
if value.is_a?(Hash)
value = symbolize_keys(value)
end
new[key.to_sym]=value
end
return new
end
puts symbolize_keys("c"=>{"a"=>2,"k"=>{"e"=>9}})
#{:c=>{:a=>2, :k=>{:e=>9}}}
Voici une petite fonction récursive pour faire une symbolisation profonde des clés:
def symbolize_keys(hash)
Hash[hash.map{|k,v| v.is_a?(Hash) ? [k.to_sym, symbolize_keys(v)] : [k.to_sym, v] }]
end