rubyのハッシュのキーのSymbolクラスへの変換の挙動が面白かったので、備忘録として書く。
前提
rubyのハッシュのキーにはどんな種類のオブジェクトでも書くことができる。 また、ハッシュの書き方には{key => value}
と書く場合と{key: value}
の二種類がある。
{key => value}
と書くと、キーには入れたオブジェクトがそのまま設定される。{key: value}
と書くと、キーはSymbolクラスに変換される。
Symbolクラスに変換されるのだが、クラスによってはto_symメソッドを持っていないので、ハッシュロケットでしか書けない場合がある。
# キーがシンボル以外の場合は =>(ハッシュロケット) で書く必要がある
# キーがシンボルの場合は : で書く必要がある
# だが、Stringクラスはto_symメソッドがあるので、 : で書いても暗黙的にSymbolクラスのオブジェクトに変換してくれる
hash1 = {"a" => 1, "b": 2}
hash1.keys.each do |key|
p "#{key} のクラス=> #{key.class}"
end
# => "a のクラス=> String"
# => "b のクラス=> Symbol"
# Integerクラスはto_symメソッドがないので、この書き方はできない
# syntax error, unexpected ':', expecting => のエラーが発生する
hash2 = {1: 1}
# ハッシュロケットで書く必要がある
hash3 = {1 => 1}
文字列aはStringクラスのままだが、文字列bはSymbolクラスのオブジェクトになっていることが分かる。
シンボルを作成できる文字列
シンボルを作成するにはコロンに続けて変数名やクラス名、メソッド名の識別子として有効な文字列を書く。(要は数字で始まったりハイフンやスペースが含まれていない文字列)
# OK
:This
:is
:_a
:pen_4
:$dollar_mark_is_ok
:@at_mark_is_ok
:"hoge" # さらには
:"fuga_piyo" # こんな風に文字列として
:"1234 5678" # シンボルを作成することも可能
# NG
:1 # syntax error, unexpected tINTEGER, expecting tSTRING_CONTENT or tSTRING_DBEG or tSTRING_DVAR or tSTRING_END
:I have a pen # syntax error, unexpected tIDENTIFIER, expecting end-of-input
:I-have-an-apple # `<main>': undefined local variable or method `have' for main:Object (NameError)
上記から推測されること
ハッシュを作成するときに{key: value}
と書く場合と、:String
と書く場合とで同じことが行われている模様。
本題
ここでやっと本題。ハッシュのキーにスペースが含まれている場合はどうなるか。
# 変数名やクラス名、メソッド名の識別子として有効な文字列ではないのでエラーとなる
hash4 = {this is: "a pen"} # => syntax error, unexpected tLABEL, expecting do or '{' or '('
# to_symメソッドがあるStringクラスのオブジェクトをキーにしているのでSymbolクラスのオブジェクトに変換してくれる
hash5 = {"this is a": "pen", "this is an" => "apple"}
# "this is a"文字列はSymbolクラスのオブジェクトになっているのでStringクラスのオブジェクトでは値がとれない
p hash5["this is a"] # => nil
# "this is an"小文字はStringクラスなので値がとれる
p hash5["this is an"] # => "apple"
# Symbolクラスのオブジェクトに変換することで値がとれる
p hash5[:"this is a"] # => "pen"
# ここでも変数名やクラス名、メソッド名の識別子として有効な文字列ではないのでエラーとなる
p hash5[:this is a] # => syntax error, unexpected tIDENTIFIER, expecting ']'
調べた結論
:String
と書いてシンボルを作成する場合や、ハッシュを作成するときに{key : value}
と書く場合、key.to_sym
メソッドが実行されている。
要はどうしても空白が含まれる文字列をSymbolオブジェクトとしてキーに設定したい場合、{"String": value}
と書いてから変換してもらえばいいちゅうことです。