Rubyでハッシュ内のすべてのキーを文字列からシンボルに変換する(最速/クリーン/ストレート)方法は何ですか?
これは、YAML を解析するときに便利です。
my_hash = YAML.load_file('yml')
私は使用できるようにしたい:
my_hash[:key]
それよりも:
my_hash['key']
Rails を使用している場合は、次の方法が適しています。
パラメータ。symbolize_keys
終わり。
そうでない場合は、コードを切り取ってください(リンクにもあります):
myhash.keys.each do |key|
myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
Ruby >= 2.5 ( docs )では、以下を使用できます。
my_hash.transform_keys(&:to_sym)
古いバージョンの Ruby を使用していますか? これは、シンボル化されたキーを使用してハッシュを新しいハッシュにコピーするワンライナーです。
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
Railsでは以下を使用できます。
my_hash.symbolize_keys
my_hash.deep_symbolize_keys
For the specific case of YAML in Ruby, if the keys begin with ':
', they will be automatically interned as symbols.
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
Output:
# /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
Rails を使用している場合は、はるかに簡単です。HashWithIndifferentAccess を使用して、文字列とシンボルの両方としてキーにアクセスできます。
my_hash.with_indifferent_access
以下も参照してください。
http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html
または、Ruby コアおよび標準ライブラリ クラスへの多くの拡張機能を含む素晴らしい「Facets of Ruby」Gem を使用することもできます。
require 'facets'
> {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
=> {:some=>"thing", :foo=>"bar}
さらに簡潔に:
Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
オブジェクトを深く記号化する方法は次のとおりです
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
私はマッシュの宝石が本当に好きです。
、mash['key']
またはmash[:key]
、またはmash.key
params.symbolize_keys
また動作します。このメソッドは、ハッシュキーをシンボルに変換し、新しいハッシュを返します。
Rails では、以下を使用できます。
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!
に変換:
{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
@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
ここには非常に多くの答えがありますが、1つのメソッドrails関数はhash.symbolize_keys
あなたは怠け者かもしれません、そしてそれをでラップしてlambda
ください:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
しかし、これはハッシュからの読み取りにのみ機能し、書き込みには機能しません。
それを行うには、次を使用できますHash#merge
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
init ブロックは必要に応じてキーを 1 回変換しますが、シンボル バージョンにアクセスした後にキーの文字列バージョンの値を更新しても、シンボル バージョンは更新されません。
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}
また、初期化ブロックでハッシュを更新しないようにすることもできます。これにより、そのようなエラーから保護されますが、逆の脆弱性が残ります。シンボル バージョンを更新しても、文字列バージョンは更新されません。
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}
したがって、これらで注意すべきことは、2 つのキー フォームを切り替えることです。1つに固執します。
短いワンライナー fwiw:
my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
次のようなものは機能しますか?
new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }
ハッシュをコピーしますが、ほとんどの場合、それは気にしません。おそらく、すべてのデータをコピーせずにそれを行う方法があります。
これはどう:
my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))
# my_hash['key'] => "val"
# my_hash[:key] => "val"
これは、メソッドを使用していて定義されmruby
ていない人向けです。symbolize_keys
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
メソッド:
String
RuntimeError
バニラ ルビー ソリューションが必要で、私がActiveSupport
ここにアクセスできない場合は、ディープ シンボライズ ソリューションです (以前のものと非常によく似ています)。
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
文字列 = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]
記号 = [ ]
文字列.各 {|x| シンボル.プッシュ(x.インターン)}
したがって、これはおそらく、Ruby で配列内の文字列をシンボルに変換する最も簡単な方法です。文字列の配列を作成してから、新しい変数を作成し、変数を空の配列に設定します。次に、「.each」メソッドで作成した最初の配列の各要素を選択します。次に、ブロック コードを使用して新しい配列のすべての要素を「.push」し、「.intern または .to_sym」を使用してすべての要素をシンボルに変換します。
シンボルは、コード内でより多くのメモリを節約し、一度しか使用できないため、高速です。シンボルは、ハッシュのキーに最も一般的に使用されます。これは素晴らしいことです。私は最高の Ruby プログラマーではありませんが、この形式のコードは私を大いに助けてくれました。誰かがより良い方法を知っている場合は、共有してください。この方法をハッシュにも使用できます。
Facets の Hash#deep_rekeyも良いオプションです。特に:
サンプル:
require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
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}
Rails を使用していないときは、このワンライナーが気に入っています。処理中に 2 番目のハッシュを作成して 2 つのデータ セットを保持する必要がないからです。
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 は削除されたキーの値を返します
任意のハッシュに対して再帰的に symbolize_keys を実行します。
class Hash
def symbolize_keys
self.is_a?(Hash) ? Hash[ self.map { |k,v| [k.respond_to?(:to_sym) ? k.to_sym : k, v.is_a?(Hash) ? v.symbolize_keys : v] } ] : self
end
end