Railsチュートリアル 第4章 - Rails風味のRubyを学ぶ…ハッシュとシンボル

# ハッシュとは

一言で言えば、「インデックス（添字）として、整数（スカラー値）以外のデータ型を用いる事ができる配列」のことです。

Rubyでは「ハッシュ」という語が用いられていますが、以下のような別名も存在します。

* 連想配列（Perlなど）
* マップ（Javascriptなど）
* 辞書（Pythonなど）

# ハッシュを定義するには

以下は各要素を角カッコ`[]`を使って定義する方法です。

```irb:
>> user = {}  # {}は空のハッシュ
=> {}
>> user["first_name"] = "Michael"  # キー…"first_name"、値…"Michael"
=> "Michael"
>> user["last_name"] = "Hartl"  # キー…"last_name"、値…"Hartl"
=> "Hartl"
>> user["first_name"]  # ハッシュの要素へは、配列の要素と類似する方法でアクセスできる
=> "Michael"
>> user  # ハッシュのリテラル表記
=> {"first_name"=>"Michael", "last_name"=>"Hartl"}
```

以下はハッシュロケット`=>`を使って定義する方法です。角カッコを使った定義よりも簡単な書き方です。Rubyにおいては、ハッシュの最初と最後にスペースを入れる記法が好まれているそうです。

```irb:
>> user = { "first_name" => "Michael", "last_name" => "Hartl" }
=> {"first_name"=>"Michael", "last_name"=>"Hartl"}
```

# シンボル

シンボルとは、ソースコード上の文字列と内部処理に使う一意の数値を1対1に割り付けたものです。「シンボルとは何かを端的に説明するとどうなるか」というテーマについて、先人は以下のように言及しています。

* 「文字列の皮を被った数値」（ @Kta-M さん）
* 「プログラム内の名前という概念を表すオブジェクト」( @yugui さん)

[参考記事:Rubyの文字列とシンボルの違いをキッチリ説明できる人になりたい](https://qiita.com/Kta-M/items/53a13ef60e14fcb41193)

```irb:
>> :foobar.object_id
=> 7768988

>> a = :foobar
=> :foobar  # => 7768988
>> b = :foobar
=> :foobar  # => 7768988
>> a.equal?(b)
=> true

>> a = "foobar"
=> "foobar"  # => 47206601248540
>> b = "foobar"
=> "foobar"  # => 47206601227140
>> a.equal?(b)
=> false
```

文字列式は、同じ文字列リテラルを指すものであっても、評価されるたびに違うオブジェクトを生成します。一方で、同じシンボルに対しては、何度評価されても常に同じオブジェクトを返します。その違いは、`equal?`メソッドの戻り値の違いにも現れています。

## ハッシュのキーとしてシンボルを用いる

```irb:
>> user = { :name => "Michael Hartl", :email => "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
>> user[:name]  # :nameというキーに対応する値
=> "Michael Hartl"
>> user[:password]  # 未定義のキーに対する値
=> nil
>> user["password"]
=> nil
```

Rubyのハッシュでは、一般に、キーとしてシンボルを用います（「名前という概念を定義するための存在」というシンボルの存在意義からして自然な成り行きですよね）。定義済みのハッシュの未定義のキーに対する値を参照しに行った場合、`nil`を返します。

```irb:
>> user = { name: "Michael Hartl", email: "michael@example.com" }
=> {:name=>"Michael Hartl", :email=>"michael@example.com"}
```

実用的には、上記のような「キーを表すシンボルとハッシュロケットの組を、シンボル名とコロンに置き換えた表記」も広く用いられます。Pythonなどで見る表記に似ていますね。

## ハッシュのハッシュ

ハッシュの値として、配列や別のハッシュを与えることも可能です。Webレスポンス、JSON、YAMLなどを扱う場合に頻出するデータ構造です。Railsチュートリアル 第4章では、「[ハッシュのハッシュ](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#code-nested_hashes)」を扱っています。

```irb:
>> params = {}  # 空のハッシュを定義する
=> {}
>> params[:user] = { name: "Michael Hartl", email: "mhartl@example.com" }
=> {:name=>"Michael Hartl", :email=>"mhartl@example.com"}
>> params                                              
=> {:user=>{:name=>"Michael Hartl", :email=>"mhartl@example.com"}}
>> params[:user][:email]
=> "mhartl@example.com"
```

## ハッシュに対して`each`メソッドを実行し、その結果をブロックで受ける

以下は、ハッシュに対し`each`メソッドを実行し、その結果をブロックで受けて実行する例です。

```irb:
>> flash = { success: "It worked!", danger: "It failed." }
=> {:success=>"It worked!", :danger=>"It failed."}

>> flash.each do |key, value|
?>   puts "Key #{key.inspect} has value #{value.inspect}"
>> end
Key :success has value "It worked!"
Key :danger has value "It failed."
=> {:success=>"It worked!", :danger=>"It failed."}
```

ハッシュに対する`each`メソッドをブロックで受ける場合、ブロックの変数が2つあることがポイントですね。キーに対応する変数と、値に対応する変数の2つです。ハッシュに対する`each`メソッドは、「キーと値のペア」を単位として行われるのです。

# `inspect`メソッドと、モジュール関数`p`

`inspect`メソッドは、オブジェクトを人間が読める形式に変換して返すメソッドです。「人間が読める形式」が何を指すかは、オブジェクトの型によって様々です。

```irb:
>> puts (1..5).to_a.inspect
[1, 2, 3, 4, 5]
=> nil

>> puts :name, :name.inspect
name
:name
=> nil

>> puts "It worked!", "It worked!".inspect
It worked!
"It worked!"
=> nil

puts Time.new.inspect
2019-09-12 13:26:36 +0000
=> nil
```

`inspect`メソッドと関係して、`p`というモジュール関数[^モジュール関数] [^Railsチュートリアルにおけるpの言及について]が存在します。[Rubyリファレンスマニュアル](https://docs.ruby-lang.org/ja/latest/class/Kernel.html#M_P)によれば、「引数の`inspect`メソッドの返り値と改行を順番に出力します」とのことです。戻り値がnilである`puts`とは異なり、`p`の戻り値は「引数のオブジェクト」となります。以下、`p`の実行例です。

```irb:
>> p :name
:name
=> :name

> p "It worked!"           
"It worked!"
=> "It worked!"

>> p Time.new
2019-09-12 13:29:48 +0000
=> 2019-09-12 13:29:48 +0000
```

[^モジュール関数]: ひとまず「メソッドと同様に実行できるが、レシーバを必要としないもの」と認識しています。`puts`もモジュール関数ですね。
[^Railsチュートリアルにおけるpの言及について]: [Railsチュートリアル](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#sec-hashes_and_symbols)における「`p`メソッドというショートカット」という言い回しには、なんとなく違和感がありました。

# [演習 - ハッシュとシンボル](https://railstutorial.jp/chapters/rails_flavored_ruby?version=5.1#sec-exercises_hashes_and_symbols)

## 1.1 キーが’one’、’two’、’three’となっていて、それぞれの値が’uno’、’dos’、’tres’となっているハッシュを作ってみてください。

ハッシュロケットを使った記法でハッシュを作ってみます。

```irb:
>> es = { 'one' => 'uno', 'two' => 'dos', 'three' => 'tres' }
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}
>> p es
{"one"=>"uno", "two"=>"dos", "three"=>"tres"}
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}
```

ハッシュの変数名はesとしてみました。

## 1.2 その後、ハッシュの各要素をみて、それぞれのキーと値を"’#{key}’のスペイン語は’#{value}’"といった形で出力してみてください。

1.1の続きです。`each`メソッドとブロック記法を用います。Rails Consoleで日本語入力が可能な環境を整備するのが面倒だったので、メッセージは英語です。

```irb:
>> es.each do |eng, esp|
?>   p "#{eng} is #{esp} in Spanish."
>> end
"one is uno in Spanish."
"two is dos in Spanish."
"three is tres in Spanish."
=> {"one"=>"uno", "two"=>"dos", "three"=>"tres"}
```

## 2.1 person1、person2、person3という３つのハッシュを作成し、それぞれのハッシュに:firstと:lastキーを追加し、適当な値 (名前など) を入力してください。

```irb:
>> person1 = { :first => "Michael", :last => "Hartl" }
=> {:first=>"Michael", :last=>"Hartl"}

>> person2 = { :first => "Foo", :last => "Bar" }
=> {:first=>"Foo", :last=>"Bar"}

>> person3 = { first: "Hoge", last: "Fuga" }
=> {:first=>"Hoge", :last=>"Fuga"}
```

person3のみ、省略記法を用いて定義してみました。

## 2.2 その後、次のようなparamsというハッシュのハッシュを作ってみてください。

> 1.) キーparams[:father]の値にperson1を代入
> 2). キーparams[:mother]の値にperson2を代入
> 3). キーparams[:child]の値にperson3を代入
> 最後に、ハッシュのハッシュを調べていき、正しい値になっているか確かめてみてください。(例えばparams[:father][:first]がperson1[:first]と一致しているか確かめてみてください)

```irb:
>> params = { father: person1, mother: person2, child: person3 }
=> {:father=>{:first=>"Michael", :last=>"Hartl"}, :mother=>{:first=>"Foo", :last=>"Bar"}, :child=>{:first=>"Hoge", :last=>"Fuga"}}

>> params[:father][:first].equal?(person1[:first])
=> true  # => 47206604256140

>> params[:child][:last].equal?(person3[:last])
=> true  # => 47206604128880
```

* params[:father][:first]とperson1[:first]
* params[:child][:last]とperson3[:last]

以上はオブジェクトとしても一致しています。

## 3. userというハッシュを定義してみてください。このハッシュは３つのキー:name、:email、:password_digestを持っていて、それぞれの値にあなたの名前、あなたのメールアドレス、そして16文字からなるランダムな文字列が代入されています。

今回は、空のハッシュから順を追って定義してみることにします。

```irb:
>> user = {}
=> {}

>> user[:name] = 'Foo bar'
=> "Foo bar"

>> user[:email] = 'example@example.jp'
=> "example@example.jp"

>> user[:password_digest] = [*'0'..'9', *'a'..'z', *'A'..'Z'].shuffle[0..15].join
=> "DjaIs4nV1Jh6OYKt"

>> p user
{:name=>"Foo bar", :email=>"example@example.jp", :password_digest=>"DjaIs4nV1Jh6OYKt"}
=> {:name=>"Foo bar", :email=>"example@example.jp", :password_digest=>"DjaIs4nV1Jh6OYKt"}
```

「16文字からなるランダムな文字列」というお題を出されたので、`[*'0'..'9', *'a'..'z', *'A'..'Z'].shuffle[0..15].join`という実装をしてみました。これは、「英大文字・英小文字・数字各1文字ずつから成る配列をシャッフルした上で、先頭15要素を取り出し、連結して文字列にしたものを返す」というメソッドチェーンです。

なお、例えば`[*'a'..'z']`は、内部的には`('a'..'z').to_a`を呼び出すことになります。

## 4. Ruby API (訳注: もしくはるりまサーチ) を使って、`Hash`クラスの`merge`メソッドについて調べてみてください。次のコードを実行せずに、どのような結果が返ってくるか推測できますか? 推測できたら、実際にコードを実行して推測があっていたか確認してみましょう。 

> ```irb:演習コード
>   { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
> ```

* [Rubyリファレンスマニュアル（るりま）におけるHash#mergeの解説](https://docs.ruby-lang.org/ja/latest/class/Hash.html#I_MERGE)

以下の記述がポイントですね。

> self と others に同じキーがあった場合はブロック付きか否かで 判定方法が違います。ブロック付きのときはブロックを呼び出してその返す値を重複キーに対応する値にします。ブロック付きでない 場合は常に others の値を使います。

ブロックらしき記法は見当たらないので、`merge`メソッドの戻り値は`{ "a" => 100, "b" => 300 }`になるかと思います。

```irb:結果
>>  { "a" => 100, "b" => 200 }.merge({ "b" => 300 })
=> {"a"=>100, "b"=>300}
```

予想通りでした。

### 余談 - ブロック付きの`merge`メソッド

[Rubyリファレンスマニュアル（るりま）におけるHash#mergeの解説](https://docs.ruby-lang.org/ja/latest/class/Hash.html#I_MERGE)には、以下の記述があります。

> ブロック付きのときはブロックを呼び出してその返す値を重複キーに対応する値にします。

モノは試しなので、実際にやってみましょう。

```irb:
>> { "a" => 100, "b" => 200 }.merge({ "b" => 300 }) do |key, foo_val, bar_val|
?>   foo_val - bar_val
>> end
=> {"a"=>100, "b"=>-100}
```

200-300は-100です。重複するキー`"b"`の値に対し、確かにブロック内容通りの演算がされていますね。

# Railsの`stylesheet_link_tag`メソッドの呼び出し

Railsチュートリアルには、以下のコードが登場します。

```erb:
<%= stylesheet_link_tag 'application', media: 'all',
                                       'data-turbolinks-track': 'reload' %>
```

Railsチュートリアルによれば、この部分は、Railsの`stylesheet_link_tag`メソッドを2つの引数で呼び出しています。重要なのは以下の点です。

* メソッド呼び出しの丸括弧`()`が省略されている
  * `p({})`のような特別な場合[^メソッド呼び出しの丸括弧が必須となる例]を除き、Rubyでは、メソッドの引数を明示する丸括弧は必須ではない
* ハッシュの波括弧`{}`が省略されている
  * ハッシュがメソッドの最後の引数である場合、波括弧`{}`は省略できる
* 式が2行にわたっている
  * Rubyでは、式を完了させない状態で改行[^式を完了させない状態で改行]させれば、複数行にわたる式を記述できる

引数が3つあるように見えますが、`media: 'all', 'data-turbolinks-track': 'reload'`というのは、実は一つのハッシュであり、これで一つの引数です。

よって、例えば以下の2つの式は等価となります。

```irb:
stylesheet_link_tag 'application', media: 'all',
                                       'data-turbolinks-track': 'reload'
```

```irb:
stylesheet_link_tag('application',
                    {media: 'all',
                    'data-turbolinks-track': 'reload' })
```

[^メソッド呼び出しの丸括弧が必須となる例]: `p({})`の場合、メソッド呼び出しの丸括弧が明示的に記述されていなければ、`{}`が引数として解釈されません。
[^式を完了させない状態で改行]: 行の最後に演算子(`+`など)やカンマ`,`などがある場合、改行があっても式は完了しません。