[ruby]Hashのキー(key)をシンボル(symbol)に変換する


Ruby2.1で確認

2つのHashを比較する際に、片方のHashはキーがシンボル、もう片方のHashはキーが文字列になっていたので、シンボルに統一するように変換することにしました。

その際にちょっとハマったのでメモ。

キーをシンボルに変換するにはactive_supportのsymbolize_keysメソッドが使えます。

ですが、深い階層のHashだと2階層目以降が変換されません。

2階層目以降も変換する用にはdeep_symbolize_keysメソッドが用意されています。

逆のパターンとしてシンボルを文字列に変換する場合はstringify_keys、deep_stringify_keysが用意されています。

以下サンプルです。

また、Hashの中にHashの配列が含まれていた場合はdeep_symbolize_keysメソッドを使っても変換できませんでした。

その場合はオーバーライドするか、別名のメソッドを作成する必要があるようです。

Qiitaの以下の記事を参考にさせていただきました。

Ruby 途中にArrayが入っても辿れるdeep_symbolize_keys

# diff.rb
require 'rubygems'
require 'hashdiff'
require 'active_support'
require 'active_support/core_ext'

sample = {
  "aa" => {
    "bb" => {
      "cc" => [
        111,
        123,
      ],
    },
  },
}

# 文字列キーをシンボルに変換
p sample.symbolize_keys
# => {:aa=>{"bb"=>{"cc"=>["111", "123"]}}} # 第一階層しか変換されない
p sample.deep_symbolize_keys
# => {:aa=>{:bb=>{:cc=>["111", "123"]}}}

# シンボルキーを文字列に変換
p sample.deep_symbolize_keys.stringify_keys
# => {"aa"=>{:bb=>{:cc=>[111, 123]}}} # 第一階層しか変換されない
p sample.deep_symbolize_keys.deep_stringify_keys
# => {"aa"=>{"bb"=>{"cc"=>[111, 123]}}}

sample = {
  "aa" => {
    "bb" => {
      "cc" => [
        {
          "dd" => 111,
        },
        {
          "ee" => 123,
        },
      ],
    },
  },
}

p sample.deep_symbolize_keys
# => {:aa=>{:bb=>{:cc=>[{"dd"=>111}, {"ee"=>123}]}}} # 配列が途中に入ると変換されない

class Hash
  def deep_symbolize_keys
    self.each_with_object({}) do |(key, val), memo|
      if val.is_a?(Array)
        val = val.map{|v| v.deep_symbolize_keys}
      elsif val.is_a?(Hash)
        val = val.deep_symbolize_keys
      end
      memo[key.to_s.to_sym] = val
    end
  end
end

p sample.deep_symbolize_keys
# => {:aa=>{:bb=>{:cc=>[{:dd=>111}, {:ee=>123}]}}} # 配列を含んでいても変換される

参考