【Ruby】ハッシュを初めから丁寧に理解してマスターしよう!

Ruby

ハッシュとは?

ハッシュとは、キーと値の組み合わせをセットにして複数のデータを管理するオブジェクトのことです。

ハッシュは、次の画像のように「果物の名前、値段」(Apple, 150円)がセットになって管理されている様なイメージになります。そして、fruits号に複数のキーと値の組み合わせが代入されます。

ハッシュのイメージ

配列では、添字を指定することによって、要素の変更などを行なっていましたが、ハッシュではキーを指定することで値を変更します。
配列がまだよく分からないという方は、【Ruby】配列の基礎を初心者に対して徹底的に分かりやすく解説!を参考にして下さい。

Rubyでハッシュを作ってみよう!
リンクをコピーしました

ハッシュは、{}を使って定義します。
後ほどシンボルの箇所で詳しく説明しますが、キーと値をハッシュに格納する際にハッシュロケット(=>)を使って記述します。
ハッシュに格納するキーと値の組み合わせが複数ある場合は、{}内にカンマ区切りで記述します。

irb
1
2
3
4
5
6
7
8
# 空のハッシュを定義
 {}

# キーと値の組み合わせを3つの格納するハッシュ
{ "キー1" => "値1", "キー2" => "値2", "キー3" => "値3" }

# 変数aに3つのキーと値の組み合わせが格納されたハッシュを代入する
a = { "キー1" => "値1", "キー2" => "値2", "キー3" => "値3" }

上記はどれもHashクラスのオブジェクトになります。オブジェクトのクラスを調べることができる「classメソッド」で確認してみます。

irb
1
2
3
4
5
6
7
8
9
10
11
{}.class
=> Hash

{ "キー1" => "値1", "キー2" => "値2", "キー3" => "値3" }.class
=> Hash

 a = { "キー1" => "値1", "キー2" => "値2", "キー3" => "値3" }
=> {"キー1"=>"値1", "キー2"=>"値2", "キー3"=>"値3"}

a.class
=> Hash

このことからハッシュがHashクラスのオブジェクトということが分かります。

Rubyではハッシュという名前ですが、他の言語では連想配列やマップと呼ばれる場合があります。

※irbは省略させて頂いています。また、Hash.newでもハッシュを作成することが出来ますが、この記事では特に扱いません。

ハッシュの{}とブロックの{}の違い
リンクをコピーしました

ハッシュを定義する際に{}を使っていましたが、Rubyでは他にもブロックで{}を使います。この2つは書き方を誤ってしまうと、Ruby自身がハッシュの{}なのかブロックの{}なのか分からなくなる場合があります。

Rubyでは、メソッド呼び出し時に()を省略する事が出来ます。

irb
1
2
3
4
5
6
7
8
# ()ありのメソッド呼び出し
puts ('Hello')
Hello=> nil

# ()を省略してメソッドを呼び出し
puts 'Hello'
Hello=> nil

putsメソッドについて詳しく知りたい方は、putsメソッドを徹底解説!を参考にして下さい。

これは自作で定義したメソッドも適用されます。

irb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 自作でメソッドを定義
def hello(name)
  puts "#{name},hello!"
end

# ()ありのメソッド呼び出し
hello("taro")
taro,hello!
=> nil

# ()を省略してメソッド呼び出し
hello "taro"
taro,hello!
=> nil

メソッドの第一引数がハッシュかつ、()を省略してメソッドを呼び出す場合、Rubyがハッシュの{}ではなく、ブロックの{}と解釈してエラーが出てしまいます。

ruby
1
2
3
4
5
6
7
8
9
# サンプルコードでエラーを検証
def buy_ice(options={}, menu)
  puts options
end

# ()を省略してbuy_iceメソッドを呼び出すと、ハッシュの{}がブロックの{}と解釈されるのでエラーが起こる
buy_ice {'Strawberry' => true, 'Chocolate' => true}, 'ice'

SyntaxError (syntax error, unexpected =>, expecting '}')

メソッド引数の第一引数がハッシュの場合は、()をつけてメソッドを呼び出す事でブロックの{}だと解釈されずにメソッドを呼び出す事ができます。

ruby
1
2
3
4
# ()を省略せずにメソッドを呼び出すとエラーは起こらない
buy_ice( { 'Strawberry' => true, 'Chocolate' => true }, 'ice')
{"Strawberry"=>true, "Chocolate"=>true}
=> nil

この様にメソッドの第一引数がハッシュの場合は、()を省略せずにメソッドを呼び出しましょう。

ruby
1
2
3
4
5
6
7
8
9
# 第二引数がハッシュの場合
def buy_ice(menu, options={})
  puts options
end

# メソッド呼び出し時に{}も省略する事が出来る
buy_ice 'ice', 'Strawberry' => true, 'Chocolate' => true 
{"Strawberry"=>true, "Chocolate"=>true}
=> nil

ハッシュの基本的な操作
リンクをコピーしました

ハッシュは、キーを指定することによって値を変更したり取得する事が出来ます。

ハッシュイメージ

上記のイメージのコードに置くと、次の様になります。

irb
1
fruits = { "Apple" => "150円", "Orange" => "100円", "Melon" => "600円", "Grape" => "700円" }

このコードを使ってハッシュの基本的な操作を解説させて頂きます。

要素の追加と変更方法
リンクをコピーしました

ハッシュに新たに要素を追加するには、下記の様な構文を使います。

ruby
1
2
3
4
5
6
7
8
9
10
# ハッシュに新たな要素を追加する
ハッシュ[キー] = 追加する値

# イチゴを追加する
fruits['Strawberry'] = "500円"
=> "500円"

# イチゴが追加されているか確認する
fruits
=> {"Apple"=>"150円", "Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"500円"}

ハッシュの中に同名のキーが存在していた場合に、元の値は新たな値に上書きされるので注意してください。

要素の変更をする際には、下記の様な構文になります。

ruby
1
2
3
4
5
6
7
8
9
ハッシュ[変更する要素のキー] = 変更する値

# 先ほどのイチゴの値段を500円から600円に変更する
fruits["Strawberry"] = '600円'
=> "600円"

# 変更されているか確認する
fruits
=> {"Apple"=>"150円", "Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

要素の取得方法
リンクをコピーしました

要素を取得するには、下記の様な構文になります。
ハッシュは、大量のキーと値が格納されていてもハッシュの内部構造によって高速に指定したキーの値を取得する事が可能です。

ruby
1
2
3
4
5
ハッシュ[キー]

# キーの"Apple"から値段を取得する
fruits["Apple"]
=> "150円"

先ほどのイメージの中で各果物の値段を取得するには、このように指定してあげると欲しい値段を取り出すことが出来ます。
ハッシュから値段を取得する

また、指定したキーがない場合はnilを返します。

irb
1
2
fruits["pear"]
=> nil

ハッシュの要素数を調べる方法
リンクをコピーしました

ハッシュの要素数を調べるには、sizeメソッドを使います。(エイリアスはlengthメソッド)

irb
1
2
3
4
5
6
7
8
9
10
11
# ハッシュの要素数を調べる
{}.size
=> 0

# ハッシュが格納されているfruitsの中身を確認する
fruits
=> {"Apple"=>"150円", "Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

# fruitsの要素数を調べる
fruits.size
=> 5

要素を削除する方法
リンクをコピーしました

ハッシュの要素を削除するには、deleteメソッドを使います。指定したキーに対応する要素を削除します。

irb
1
2
3
4
5
6
7
8
9
10
11
# Appleに対応する要素を削除する
fruits.delete('Apple')
=> "150円"

# Appleと'150円'の組み合わせが削除されている
fruits
=> {"Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

# 指定したキーがない場合nilが返る
 fruits.delete('pear')
=> nil

戻り値は、削除した要素になります。指定したキーがない場合はnilが返ります。

deleteにブロックを渡してあげると、指定したキーがない場合nillではなく、ブロックの戻り値にする事が出来ます。

irb
1
2
fruits.delete('pear') { |key| " #{key}の対応する要素はありません"}
=> " pearの対応する要素はありません"

ハッシュを使った繰り返し処理
リンクをコピーしました

ハッシュはeachメソッドを使うと、キーと値を順番に取得する事が出来ます。配列の時とは違いブロック引数がキーと値の2種類になっているので注意してください。

ruby
1
2
3
4
5
6
7
8
9
10
# fruitsに格納されている順番にkey, valueにそれぞれキーと値を渡してブロック内の処理を実行させている
fruits.each do | key, value |
  puts "#{key}は、#{value}"
end

Orangeは、100
Melonは、600
Grapeは、700
Strawberryは、600
=> {"Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

ブロックに渡した引数は「key, value」の2個でしたが、引数を1個だけにすると、キーと値が配列に格納されます。

ruby
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ブロックに渡す引数を1つにすると配列に格納される
fruits.each do | key_value |
  puts "#{key_value}"
end

# キーと値はそれぞれ添字0,1に格納される
["Orange", "100円"]
["Melon", "600円"]
["Grape", "700円"]
["Strawberry", "600円"]
=> {"Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

# キーと値をブロック内で使うには、配列から取り出す処理を追加
fruits.each do | key_value |
  key = key_value[0]
  value = key_value[1]
  puts "#{key}#{value}"
end

Orange100
Melon600
Grape700
Strawberry600
=> {"Orange"=>"100円", "Melon"=>"600円", "Grape"=>"700円", "Strawberry"=>"600円"}

ハッシュとシンボル
リンクをコピーしました

現在ハッシュのキーには文字列を使っていますが、シンボルを使うことが推奨されています。まずは文字列とシンボルの違いについて理解していきましょう。

シンボルとは?
リンクをコピーしました

シンボルとは、コロン(:)を使って任意の名前を定義します。シンボルは、シンボルクラスのオブジェクトです。

ruby
1
2
3
4
5
6
7
8
:シンボルの名前

# シンボルの例
:name

# シンボルクラスのオブジェクト
:name.class
=> Symbol

シンボルと文字列の違いとは?
リンクをコピーしました

シンボルはSymbolクラスのオブジェクトで、文字列はStringクラスのオブジェクトです。

シンボルは表面的には文字列と似ていますが、シンボルの中身は文字列とは違い整数で管理されています。その為、文字列よりも高速に処理出来ます。

シンボルは、「同じシンボルであれば同じオブジェクト」という特徴があります。実際に、オブジェクトのidを調べる「object_idメソッド」を使って見ていきましょう。

ruby
1
2
3
4
5
6
7
8
9
# 文字列は、同じ文字列でもオブジェクトidが違う
'pikawaka'.object_id => 70151245202480
'pikawaka'.object_id => 70151245128760
'pikawaka'.object_id => 70151248681680

# シンボルは同じシンボルであればオブジェクトidが同じ
:pikawaka.object_id => 1310428
:pikawaka.object_id => 1310428
:pikawaka.object_id => 1310428

同じシンボルが同じオブジェクトである事は、メモリの使用率などが良くなり文字列よりも処理の速度が上がります。これはハッシュのキーにも当てはまります。

また、シンボルの特徴にイミュータブルで文字列の様に破壊的な変更は出来ない為、変更されては困るものに使う事が出来ます。

ハッシュのキーにシンボルを使う
リンクをコピーしました

シンボルをハッシュのキーに使うことによって、文字列よりも高速に値を取り出したり、イミュータブルで破壊的な変更がされないのでハッシュのキーを勝手に変更される事がありません。

ハッシュのキーには文字列ではなく、シンボルを使う様にしましょう。

ruby
1
2
3
4
5
6
7
8
9
10
11
12
# 文字列をハッシュのキーにした場合
fruits = { "Apple" => "150円", "Orange" => "100円", "Melon" => "600円", "Grape" => "700円" }

# 文字列でハッシュを取り出す
fruits[ "Apple"]

# シンボルをハッシュのキーにする場合
fruits = { :Apple => "150円", :Orange => "100円", :Melon => "600円", :Grape => "700円" }

# シンボルでハッシュを取り出す
fruits[:Apple]
=> "150円"

現在ハッシュロケット(=>)を使っていますが、キーをシンボルにした場合は、ハッシュロケットを省略する事ができます。

シンボルのコロンの位置が左から右に変わるので注意してください。

ruby
1
2
3
4
5
6
# ハッシュロケットがある場合
fruits = { :Apple => "150円", :Orange => "100円", :Melon => "600円", :Grape => "700円" }

# ハッシュロケットを省略する場合、コロンの位置を左から右に変更する
fruits = { Apple: "150円", Orange: "100円", Melon: "600円", Grape: "700 円" }
=> {:Apple=>"150円", :Orange=>"100円", :Melon=>"600円", :Grape=>"700円"}

ハッシュロケット(=>)は、Ruby1.8以前でハッシュを生成する際に使われていた記法なので、シンボルを使う際は上記の様な書き方にしましょう。

ruby
1
2
3
4
5
6
7
8
# ハイフンが入っていると下記の指定だとエラーが起こる
{ hoge-hoge: 1 }
syntax error, unexpected ':', expecting =>

# Ruby2.2ではシングルクォートかダブルクォートで囲って使う事が出来る
{ 'hoge-hoge': 1 }
=> {:"hoge-hoge"=>1}
まとめ

  • ハッシュは、キーと値の組み合わせをセットにして複数のデータを管理するHashクラスのオブジェクトのことです。
  • ハッシュのキーには、文字列ではなくシンボルを使う様にしましょう。
  • シンボルの中身は整数で管理されている為、文字列よりも高速の処理を行う事ができる。