Quel est le moyen (le plus rapide/le plus simple/le plus simple) de convertir toutes les clés d'un hachage de chaînes en symboles en Ruby?
Cela serait pratique lors de l'analyse de YAML.
my_hash = YAML.load_file('yml')
J'aimerais pouvoir utiliser:
my_hash[:key]
Plutôt que:
my_hash['key']
Si vous voulez un one-liner,
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
copiera le hachage dans un nouveau avec les clés symbolisées.
Voici une meilleure méthode si vous utilisez Rails:
params. symbolize_keys
La fin.
Si vous ne l'êtes pas, extrayez leur code (c'est aussi dans le lien):
myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
Dans le cas spécifique de YAML en Ruby, si les clés commencent par ':
', elles seront automatiquement internées sous forme de symboles.
require 'yaml' require 'pp' yaml_str = " connections: - Hôte: Host1.example.com port: 10000 - Hôte: Host2. exemple.com port: 20000 " yaml_sym =" : connexions: -: hôte: hôte1.exemple.com : port: 10000 -: hôte : Host2.example.com : Port: 20000 " Pp yaml_str = YAML.load (yaml_str) Met yaml_str.keys.first.class Pp yaml_sym = YAML.load ( yaml_sym) met yaml_sym.keys.first.class
Sortie:
# /opt/Ruby-1.8.6-p287/bin/Ruby ~/test.rb {"connections" => [{"port" => 10000, "Host" => "Host1.example.com"}, {"port" => 20000, "Host" => "Host2.example.com"}]} Chaîne {: connexions => [{: port => 10000,: Host => "Host1.example.com"}, {: port => 20000,: Host => "Host2.example.com"}]} Symbole
Encore plus laconique:
Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
si vous utilisez Rails, c'est beaucoup plus simple: vous pouvez utiliser un HashWithIndifferentAccess et accéder aux clés à la fois sous forme de chaîne et sous forme de symboles:
my_hash.with_indifferent_access
voir également:
_ { http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html } _
Ou vous pouvez utiliser l’impressionnant "Facets of Ruby" Gem, qui contient de nombreuses extensions des classes Ruby Core et Standard Library.
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" }
Depuis Ruby 2.5.0
, vous pouvez utiliser Hash#transform_keys
ou Hash#transform_keys!
.
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
Voici un moyen de symboliser profondément un objet
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
J'aime vraiment le Mash gem.
vous pouvez faire mash['key']
ou mash[:key]
ou mash.key
params.symbolize_keys
fonctionnera également. Cette méthode transforme les clés de hachage en symboles et renvoie un nouveau hachage.
Une modification à la réponse @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
Si vous utilisez json et voulez l'utiliser comme un hachage, vous pouvez le faire en Ruby:
json_obj = JSON.parse(json_str, symbolize_names: true)
symbolize_names: si défini sur true, renvoie les symboles pour les noms (clés) dans un objet JSON. Sinon, les chaînes sont retournées. Les chaînes sont les valeurs par défaut.
Ceci est mon unique doublure pour les hachages imbriqués
def symbolize_keys(hash)
hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
Autant de réponses ici, mais la seule méthode de la fonction Rails est hash.symbolize_keys
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
Convertit en:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
Dans le cas où la raisonvous devez faire cela parce que vos données provenaient de JSON, vous pouvez ignorer cette analyse en passant simplement l'option :symbolize_names
lors de l'acquisition de JSON.
Aucun rail requis et fonctionne avec Ruby> 1.9
JSON.parse(my_json, :symbolize_names => true)
Vous pourriez être paresseux et l'envelopper dans une lambda
:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
Mais cela ne fonctionnerait que pour lire à partir du hash, pas pour écrire.
Pour ce faire, vous pouvez utiliser Hash#merge
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
Le bloc init convertit les clés une fois à la demande. Toutefois, si vous mettez à jour la valeur de la version de chaîne de la clé après avoir accédé à la version du symbole, celle-ci ne sera pas mise à jour.
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}
Vous pouvez également demander au bloc init de ne pas mettre à jour le hachage, ce qui vous protégerait de ce type d'erreur, mais vous seriez tout de même vulnérable au contraire. Mettre à jour la version du symbole ne mettrait pas à jour la version de la chaîne:
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}
Donc, la chose à laquelle il faut faire attention est de basculer entre les deux formes principales. Bâton avec un.
une fwiw monocouche plus courte:
my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
Ceci est destiné aux personnes qui utilisent mruby
et pour lesquelles aucune méthode symbolize_keys
n'est définie:
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
La méthode:
String
RuntimeError
Que dis-tu de ça:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
Est-ce que quelque chose comme le travail suivant?
new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }
Cela copiera le hasch, mais vous ne vous en soucierez pas la plupart du temps. Il existe probablement un moyen de le faire sans copier toutes les données.
strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
symboles = []
strings.each {| x | symboles.Push (x.intern)}
C'est donc probablement le moyen le plus simple de convertir des chaînes en symboles dans vos tableaux dans Ruby. Créez un tableau de chaînes puis créez une nouvelle variable et définissez-la sur un tableau vide. Sélectionnez ensuite chaque élément du premier tableau créé avec la méthode ".each". Utilisez ensuite un code de bloc pour ".Appuyer" sur tous les éléments de votre nouveau tableau et utilisez ".intern ou .to_sym" pour convertir tous les éléments en symboles.
Les symboles sont plus rapides car ils économisent plus de mémoire dans votre code et vous ne pouvez les utiliser qu'une seule fois. Les symboles sont le plus souvent utilisés pour les clés dans le hachage, ce qui est génial. Je ne suis pas le meilleur programmeur Ruby, mais cette forme de code m'a beaucoup aidé. Si quelqu'un connaît un meilleur moyen de le partager, partagez-le et vous pouvez aussi utiliser cette méthode pour le hachage!
Si vous souhaitez une solution Vanilla Ruby et que, comme je n'ai pas accès à ActiveSupport
, voici une solution symbolique profonde (très similaire aux précédentes)
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
En Ruby, je trouve que c'est le moyen le plus simple et le plus facile à comprendre de transformer des clés de chaîne en symboles de hachage:
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
Pour chaque clé du hachage, nous appelons delete, ce qui la supprime du hachage (delete renvoie également la valeur associée à la clé qui a été supprimée) et nous la fixons immédiatement à la clé symbolisée.
J'aime ce one-line, quand je n'utilise pas Rails, car je n'ai pas besoin de faire un deuxième hachage et de conserver deux ensembles de données pendant que je les traite:
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 renvoie la valeur de la clé supprimée
Ce n'est pas exactement une ligne, mais cela transforme toutes les clés de chaîne en symboles, également ceux imbriqués:
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
Semblable aux solutions précédentes mais écrit un peu différemment.
Le code ne mute pas lorsque le hachage a été transmis.
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}
À partir de Psych 3.0, vous pouvez ajouter le symbolize_names: option
Psych.load("---\n foo: bar")
# => {"foo"=>"bar"}
Psych.load("---\n foo: bar", symbolize_names: true)
# => {:foo=>"bar"}
Remarque: si vous avez une version Psych inférieure à la version 3.0, symbolize_names:
sera silencieusement ignoré.
Mon Ubuntu 18.04 l’inclue hors de la boîte avec Ruby 2.5.1p57
Facets 'Hash # deep_rekey est également une bonne option, notamment:
Échantillon:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey