web-dev-qa-db-ja.com

ハッシュで文字列を記号に変換する最良の方法

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

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

my_hash = YAML.load_file('yml')

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

my_hash[:key] 

のではなく:

my_hash['key']
231
Bryan M.

ワンライナーが必要な場合は、

my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}

キーがシンボル化されたハッシュを新しいハッシュにコピーします。

211
Sarah Mei

Railsを使用している場合、次の方法をお勧めします。

params . symbolize_keys

終わり。

そうでない場合は、それらのコードをリッピングするだけです(リンクにもあります)。

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
296
Sai

RubyのYAMLの特定のケースでは、キーが「:」で始まる場合、自動的にシンボルとしてインターンされます。

 require 'yaml' 
 require 'pp' 
 yaml_str = "
 connections:
-Host:Host1.example.com 
ポート:10000 
-ホスト:Host2.example.com 
ポート: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。ファーストクラス

出力:

#/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"}]} 
シンボル
113
jrgm

さらに簡潔:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
61
Michael Barton

railsを使用している場合は、はるかに簡単です-HashWithIndifferentAccessを使用して、文字列とシンボルの両方としてキーにアクセスできます:

my_hash.with_indifferent_access 

こちらもご覧ください:

http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


または、Ruby CoreおよびStandard Libraryクラスに対する多くの拡張機能を含む、素晴らしい「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-ハッシュ

56
Tilo

Ruby 2.5.0から Hash#transform_keys または Hash#transform_keys! を使用できます。

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
40
Sagar Pandya

http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
31
Ery

オブジェクトを深くシンボル化する方法を次に示します

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
26
igorsales

Mash gemが本当に好きです。

あなたはmash['key']、またはmash[:key]、またはmash.keyを行うことができます

20
ykaganovich

Jsonを使用していて、それをハッシュとして使用する場合は、コアRubyで次のようにできます。

json_obj = JSON.parse(json_str, symbolize_names: true)

symbolize_names:trueに設定すると、JSONオブジェクトの名前(キー)のシンボルを返します。それ以外の場合、文字列が返されます。文字列がデフォルトです。

Doc: Json#parse symbolize_names

12
Mark

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

11
Jae Cho

@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
10
Tony
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

に変換:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
8
JayJay

これはネストされたハッシュ用の私の唯一のライナーです

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
7
Nick Dobson

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

7
shakirthow

reasonを行う必要があるのは、データが元々JSONから来たためで、JSONの取り込み時に:symbolize_namesオプションを渡すだけでこの解析をスキップできます。

Railsは不要で、Ruby> 1.9で動作します

JSON.parse(my_json, :symbolize_names => true)
5
Adam Grant

あなたは怠beである可能性があり、それを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}

また、initブロックでハッシュを更新しないようにすることもできます。これにより、この種のエラーから保護されますが、反対の脆弱性は依然として存在します。シンボルバージョンを更新しても文字列バージョンは更新されません。

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つに固執します。

4
rampion

次のようなものはありますか?

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

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

3
ChrisInEdmonton

短いワンライナーfwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
3
sensadrome

これは、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を発生させます
  • 再帰的に含まれるハッシュもシンボル化します
  • 記号化されたハッシュを返します
  • 所定の場所で動作します!
2
Matteo Ragni

これはどう:

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

# my_hash['key'] => "val"
# my_hash[:key]  => "val"
2

変更する配列。

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

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

シンボル= []

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

strings.each {| x | symbols.Push(x.intern)}

コードの終わり。

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

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

1
rubyguest123

Vanilla Rubyソリューションが必要で、私が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
1
Haris Krajina

Psych3.0以降では、 symbolize_names: オプションを追加できます

Psych.load("---\n foo: bar") # => {"foo"=>"bar"}

Psych.load("---\n foo: bar", symbolize_names: true) # => {:foo=>"bar"}

注:3.0より低いPsychバージョンがある場合、symbolize_names:は黙って無視されます。

私のUbuntu 18.04には、すぐにRuby 2.5.1p57が含まれています

1

Rubyで、これはハッシュ内の文字列キーをシンボルに変換する最も簡単で理解しやすい方法であることがわかりました。

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}

ハッシュ内のキーごとにdeleteを呼び出してハッシュから削除し(delete キーに関連付けられた値を返します削除された)、すぐにこれをシンボル化されたキーに等しく設定します。

0
user4488109

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

0
nevets138

これは厳密には1行ではありませんが、すべての文字列キーをシンボルに変換し、ネストされたキーにも変換します。

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

以前のソリューションと似ていますが、記述が少し異なります。

  • これにより、ネストされたハッシュや配列を持つハッシュが可能になります。
  • ボーナスとしてキーから文字列への変換を取得します。
  • コードは渡されたハッシュを変更しません。

    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
    
0
jham
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}
0
Vlad Khomich

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

  • プロジェクトのファセットから他の砂糖を使用する場合は、
  • 暗号化されたワンライナーよりもコードを読みやすくしたい場合。

サンプル:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
0
Sergikon