文字列をハッシュのシンボルに変換する最良の方法


250

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

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

my_hash = YAML.load_file('yml')

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

my_hash[:key] 

のではなく:

my_hash['key']


80
hash.symbolize_keyshash.deep_symbolize_keysRailsを使用している場合は、この作業を行います。
Zaz 2014

もしあなたがあなたのコメントを答えに入れていたら、私はあなたに投票したでしょう。require 'rails'; hash.deep_symbolize_keysはirbまたはpryでかなりうまく機能します。:D
ダグラスG.アレン

回答:


239

ではルビー> = 2.5ドキュメント)あなたが使用することができます。

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 

5
ああ、不明確で申し訳ありません-インジェクトは呼び出し元を変更しません。my_hash = my_hash.inject({}){| memo、(k、v)|を実行する必要があります memo [k.to_sym] = v; memo}
サラ・メイ、

4
それがまさに私が探していたものです。少し変更して、いくつかの行を追加して、ネストされたハッシュにシンボルを作成しました。興味がある場合は、こちらをご覧ください:any-where.de/blog/ruby-hash-convert-string-keys-to-symbols
Matt

37
Ruby 1.9では次のeach_with_objectように使用できます:my_hash.each_with_object({}){|(k,v), h| h[k.to_sym] = v}
sgtFloyd

8
これは再帰的なハッシュを処理しません... 1回限りのものを探しますが、DRYを探しません。
baash05 2013年

8
@BryanM。私はこの議論に非常に遅れて入りました:-)しかし、.tapメソッドを使用memoして、最後に渡す必要を取り除くこともできます。私はすべてのソリューション(再帰的なソリューションも含む)のクリーンアップバージョンを作成しましたgist.github.com/Integralist/9503099
Integralist

307

Railsを使用している場合は、より良い方法を次に示します。

パラメータ。symbolize_keys

終わり。

そうでない場合は、コードを削除してください(リンクにも含まれています)。

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


45
ネストされたハッシュを象徴しません。
oma

3
リンクをsymbolize_keys新しい&機能している(Rails 3)URL に切り替えました。元々はのURLを修正しただけですがto_options、そのリンクにはドキュメントがありません。symbolize_keys実際に説明があるので、代わりにそれを使用しました。
Craig Walker、

23
deep_symbolize_keys!。レール2以上で動作
mastaBlasta 2013

14
逆の方法を知りたい人のために、hash.stringify_keys動作します。
ニック

112

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

「yaml」が必要
「pp」が必要
yaml_str = "
接続:
  -ホスト:host1.example.com
    ポート:10000
  -ホスト:host2.example.com
    ポート:20000
」
yaml_sym = "
:接続:
  -:host:host1.example.com
    :ポート:10000
  -:host:host2.example.com
    :ポート:20000
」
pp yaml_str = YAML.load(yaml_str)
yaml_str.keys.first.classを置く
pp yaml_sym = YAML.load(yaml_sym)
yaml_sym.keys.first.classを置く

出力:

#/opt/ruby-1.8.6-p287/bin/ruby〜/ test.rb
{"接続" =>
  [{"ポート" => 10000、 "ホスト" => "host1.example.com"}、
   {"ポート" => 20000、 "ホスト" => "ホスト2.example.com"}]}
ストリング
{:接続=>
  [{:port => 10000、:host => "host1.example.com"}、
   {:port => 20000、:host => "host2.example.com"}]}
シンボル

15
甘い!YAML#load_fileすべてのキーをコロンで始める必要がない文字列ではなく、すべてのキーをデフォルトでシンボルに設定する方法はありますか?
ma11hew28


60

Railsを使用している場合は、はるかに簡単です。HashWithIndifferentAccessを使用して、文字列と記号の両方としてキーにアクセスできます。

my_hash.with_indifferent_access 

も参照してください:

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


または、Rubyコアおよび標準ライブラリクラスへの多くの拡張機能が含まれている素晴らしい "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


2
実際にはそれは逆です。シンボルから文字列に変換します。シンボルに変換するには、my_hash.symbolize_keysを使用します
Espen

#symbolize_keysはRailsでのみ機能し、Ruby / irbでは機能しません。#symbolize_keysは、深くネストされたハッシュでは機能しないことにも注意してください。
Tilo 14

54

またはRuby 2.5.0を使用できるので。Hash#transform_keysHash#transform_keys!

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

transform_values apidock.com/rails/Hash/transform_valuesでも機能します。stringify_keysまたはのように値を変更するための同等の方法がないように見えるので、それは本当に素晴らしいですsymbolize_keys
Mike Vallano 2018

キーを深く象徴する方法はありますか
Abhay Kumar

これは、2018
Ivan Wang


26

ここにオブジェクトを深く象徴する方法があります

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

いい名前です。名前をdeep_symbolizeに変更しても、これを使います:)
PierrOz

20

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

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


2
それはとてもクールな逸品です!ハッシュの処理を非常に快適にします。これをありがとう!
asaaki 2011年

とても美しくシンプル。このプロジェクトの新しい開発がHashie(継続されているgithub.com/intridea/hashie)それはまだほとんど同じように動作します:github.com/intridea/hashie#keyconversion
jpalmieri

19

jsonを使用していて、それをハッシュとして使用したい場合は、コアRubyで実行できます。

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

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

Doc:Json#parse symbolize_names


symbol_hash = JSON.parse(JSON.generate(YAML.safe_load(FILENAME)), symbolize_names: true)YAMLファイルから取得した場合に、ネストされたキーを記号として持つハッシュをすばやく取得するためのかなりDRY(ただし非効率的な)方法です。
Cameron Gagnon

12

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


26
そのメソッドはコアRubyではありません。これはRailsメソッドです。
Ricardo Acras、2011

10

@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

回答をスキャンする人のためにオブジェクトを変更する理由を含めれば、役に立ちます。
Dbz

9

Railsで使用できるもの:

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

変換:

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

3
deep_symbolize_keysはRailsのハッシュ拡張に追加されていますが、Rubyコアの一部ではありません。
Ryan Crispin Heneise 2018年


7

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

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

参考までに、Railsでのみ機能します。その場合、HashWithIndifferentAccessがより良い代替手段になる可能性があります。
アダムグラント

6

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

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

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

4

あなたは怠惰で、それをaにラップすることができます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つに固執します。


3

次のようなものでしょうか?

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

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




2

これは、メソッドを定義し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
  • 再帰的に含まれるハッシュも記号化する
  • 記号化されたハッシュを返す
  • 機能します!

1
メソッドにタイプミスがあり、!in を忘れましたsymbolize_keys。それ以外の場合は正常に動作します。
Ka Mok

1

変更する配列。

strings = ["HTML"、 "CSS"、 "JavaScript"、 "Python"、 "Ruby"]

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

記号= []

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

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

コードの終わり。

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

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


2
問題は、配列ではなくハッシュに関するものでした。
Richard_G 2016

1

あなたがバニラルビーソリューションを望んでいて、私が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

Psych 3.0 以降では、symbolize_names オプションを追加できます

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

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

注:Psychのバージョンが3.0より低い場合、symbolize_names:警告なしで無視されます。

私のUbuntu 18.04には、Ruby 2.5.1p57に標準で含まれています


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

a大括弧で囲んでブロック引数を分解すると、これをさらに簡潔にすることができます。例えば私の答えを見てください。
Michael Barton

0

これは厳密には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

0

Railsを使用していないときは、この1行が好きです。これは、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

ファセットのHash#deep_rekeyも良いオプションです、特に:

  • プロジェクトのファセットから他の砂糖を使用する場合、
  • 不可解なワンライナーよりもコードの可読性を好む場合。

サンプル:

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

0

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

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

ハッシュ内の各キーについて、それを削除し、ハッシュから削除します(また、delete は削除されたキーに関連付けられた値を返します)。これをすぐに記号化されたキーと等しく設定します。


0

以前のソリューションに似ていますが、少し異なって書かれています。

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

    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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.