web-dev-qa-db-fra.com

Comment convertir un hachage Ruby afin que toutes ses clés soient des symboles?

J'ai un hash Ruby qui ressemble à:

{ "id" => "123", "name" => "test" }

Je voudrais le convertir en:

{ :id => "123", :name => "test" }
52
ed1t
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"}}
66
Victor Moroz

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.

53
mu is too short

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

39
MRifat

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)
18
Alex Popov

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
10
pje

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.

5
John Feminella

À 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.

4
Paulo Fidalgo

Si vous utilisez Rails (ou simplement ActiveSupport):

{ "id" => "123", "name" => "test" }.symbolize_keys
4
bonkydog

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)
4
Chakaitos

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!
2
David Fabreguette

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 }
2
the Tin Man

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
1
don giulio

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"}}
1
Sergikon
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}}}
1
kajal agarwal

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
0
user3250006