273

Rubyでハッシュ内のすべてのキーを文字列からシンボルに変換する(最速/クリーン/ストレート)方法は何ですか?

これは、YAML を解析するときに便利です。

my_hash = YAML.load_file('yml')

私は使用できるようにしたい:

my_hash[:key] 

それよりも:

my_hash['key']
4

31 に答える 31

316

Rails を使用している場合は、次の方法が適しています。

パラメータ。symbolize_keys

終わり。

そうでない場合は、コードを切り取ってください(リンクにもあります):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
于 2009-05-21T00:14:58.493 に答える
305

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 
于 2009-04-29T01:17:06.357 に答える
115

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
于 2009-12-18T07:48:41.520 に答える
65

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}

参照: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash

于 2011-08-27T06:02:35.843 に答える
64

さらに簡潔に:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
于 2012-04-10T14:20:00.793 に答える
27

オブジェクトを深く記号化する方法は次のとおりです

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
于 2012-08-03T01:30:28.997 に答える
21

私はマッシュの宝石が本当に好きです。

mash['key']またはmash[:key]、またはmash.key

于 2009-05-21T00:27:46.923 に答える
13

params.symbolize_keysまた動作します。このメソッドは、ハッシュキーをシンボルに変換し、新しいハッシュを返します。

于 2010-12-31T05:43:06.440 に答える
12

Rails では、以下を使用できます。

{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

に変換:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
于 2017-10-31T10:40:18.357 に答える
12

@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
于 2013-04-04T15:32:18.213 に答える
8

ここには非常に多くの答えがありますが、1つのメソッドrails関数はhash.symbolize_keys

于 2016-05-19T22:30:02.190 に答える
5

あなたは怠け者かもしれません、そしてそれをでラップして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つに固執します。

于 2009-04-28T23:20:14.843 に答える
4

短いワンライナー fwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
于 2014-01-16T15:34:00.977 に答える
4

次のようなものは機能しますか?

new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }

ハッシュをコピーしますが、ほとんどの場合、それは気にしません。おそらく、すべてのデータをコピーせずにそれを行う方法があります。

于 2009-04-28T22:49:35.133 に答える
3

これはどう:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))

# my_hash['key'] => "val"
# my_hash[:key]  => "val"
于 2010-12-17T13:57:05.233 に答える
3

これは、メソッドを使用していて定義され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
  • 再帰的に含まれるハッシュも記号化する
  • 記号化されたハッシュを返す
  • その場で動作します!
于 2015-10-24T13:23:02.730 に答える
2

バニラ ルビー ソリューションが必要で、私が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
于 2015-11-27T09:26:19.963 に答える
2

変更したい配列。

文字列 = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]

新しい変数を空の配列として作成し、シンボルを「.push」できるようにします。

記号 = [ ]

ここで、ブロックを使用してメソッドを定義します。

文字列.各 {|x| シンボル.プッシュ(x.インターン)}

コードの終わり。

したがって、これはおそらく、Ruby で配列内の文字列をシンボルに変換する最も簡単な方法です。文字列の配列を作成してから、新しい変数を作成し、変数を空の配列に設定します。次に、「.each」メソッドで作成した最初の配列の各要素を選択します。次に、ブロック コードを使用して新しい配列のすべての要素を「.push」し、「.intern または .to_sym」を使用してすべての要素をシンボルに変換します。

シンボルは、コード内でより多くのメモリを節約し、一度しか使用できないため、高速です。シンボルは、ハッシュのキーに最も一般的に使用されます。これは素晴らしいことです。私は最高の Ruby プログラマーではありませんが、この形式のコードは私を大いに助けてくれました。誰かがより良い方法を知っている場合は、共有してください。この方法をハッシュにも使用できます。

于 2015-07-05T08:20:01.107 に答える
1

Facets の Hash#deep_rekeyも良いオプションです。特に:

  • プロジェクトのファセットから他の砂糖の用途が見つかった場合は、
  • 暗号的なワンライナーよりもコードの読みやすさを好む場合。

サンプル:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
于 2016-11-02T07:12:01.357 に答える
1
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}
于 2011-07-24T09:41:00.193 に答える
1

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 は削除されたキーの値を返します

于 2015-07-02T16:05:49.213 に答える
-1

任意のハッシュに対して再帰的に 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
于 2016-02-16T14:11:53.520 に答える