ChefTips: RubyのArray(配列)とHash(連想配列)入門

# RubyのArray(配列)とHash(連想配列)入門
Chef の Cookbook を書いたり読んだりする上で、Array(配列) と Hash(連想配列)で混乱される方が多いのではないでしょうか。プログラマな方には常識だそうですが、普段コードを書かないインフラエンジニアの方が Chefを理解していく際に結構な壁になることが多いようです。わたしもそうでした。

自分自身のメモ代わりにまとめたいと思います。

## Array(配列) とは

Arrayは、イメージしやすく言うと複数のValue(値)が入っている入れ物です。順番(添字)をつけてValue(値)を管理します。順番は1からではなく、0から始まります。

```rb
# Array family_array を定義
family_array = [ "sazae", "masuo", "tarao" ]                     

# Array family_array からValue(値)を取り出す
puts family_array[0] # sazae と表示
puts family_array[1] # masuo と表示
puts family_array[2] # tarao と表示
```

## Hash(連想配列) とは

ArrayはValue(値)を管理するために添字を使いますが、Hashは添字の代わりにKeyを使います。KeyとValueのペアは "=>"（ハッシュロケット) で表現します。  

Keyは数字と違いなんらかの意味を持つので、コードが読みやすくなるという利点があります。下のサンプルコードを見てください。なんとなく、sazaeが妻で、mazuoが夫で、taraoが子供であることがコードを呼んでいる人に伝わりますよね。

```rb
# Hash family_hash を定義
family_hash = { "tsuma" => "sazae", "otto" => "masuo", "kodomo" => "tarao" }  

# Hash family_hash から値を取り出す
puts family_hash["tsuma"]   # sazae と表示
puts family_hash["otto"]    # masuo と表示
puts family_hash["kodomo"]  # tarao と表示
```

### Arrayは[]で囲む、Hashは {}で囲む

大切なことなのでもう一度いいます。Arrayは[]で囲んで、Hashは {}で囲みます。大切です。

```rb
# Array は []で囲む。
family_array = [ "sazae", "masuo", "tarao" ]  

# Hash は {}で囲む。
family_hash = { "tsuma" => "sazae", "otto" => "masuo", "kodomo" => "tarao" }  
```


## Hash の Symbol に注意

RubyのHashで混乱しやすいところは、HashのKeyに、String(文字列)とSymbolの両方使えるところです。StringもSymbolも人間から見ると、文字に見えるのですが、文法(リテラル)や内部処理の仕方が異なります。異なるので、混乱しやすいのです。

ものすごくざっくりいうと、Keyを `" "` や `' '` で囲むのがString、接頭辞 `:` がつくのがSymbolです。下のサンプルコード hash1とhash2はほぼおなじHashです。

```rb
# Keyを String で定義
hash_string = { "tsuma" => "sazae", "otto" => "masuo", "kodomo" => "tarao" } 
# 値を取り出し
puts hash_string['tsuma']  # "sazae" と表示
puts hash_string['otto']   # "masuo" と表示
puts hash_string['kodomo'] # "tarao" と表示

# Keyを Symbol で定義
hash_symbol = { :tsuma => "sazae", :otto => "masuo", :kodomo => "tarao" } 
# 値を取り出し
puts hash_string[:tsuma]  # "sazae" と表示
puts hash_string[:otto]   # "masuo" と表示
puts hash_string[:kodomo] # "tarao" と表示
```

更にややこしいことに、HashのKeyにSymbolを使う場合、KeyとValueの紐付けに `=>` (ハッシュロケット)を使わず以下のようなシンプルな表記が可能になります。  

記載はシンプルになって見やすいのですが、この表記法を知らない人には混乱のタネになります。また、あくまでも紐付けの表記が変わるだけで、値を取り出す際のリテラルは変わらないことにご注意ください。

```rb
# Keyを Symbol で定義 その2
hash_symbol = { tsuma: "sazae", otto: "masuo", kodomo: "tarao" } 
# 値を取り出し
puts hash_string[:tsuma]  # "sazae" と表示
puts hash_string[:otto]   # "masuo" と表示
puts hash_string[:kodomo] # "tarao" と表示
```


### Symbol の特徴

- Symbol とは、Ruby が内部でメソッド名などの識別に使っている数値で、任意の文字列に対して異なった値が割り当てらる。
- Symbol は、Symbolクラスのインスタンスとして実装されている
- 使い勝手と可読性は文字列と同程度で、実行が速いので、Symbolのほうがよい（らしい）

### Symbol と String の違い

> Symbol は文字列と同じような感覚で用いることができる。ただし、等値性に関して、異なることに注意。
>
> - == は値が等しい場合に真となり、
> - equal? はインスタンスが同じ場合に真となる。

試してみる。
↓文字列の場合、値は等しくても、別のインスタンスとして認識されている  

```rb
mojiretu1 = "mojiretu"
mojiretu2 = "mojiretu"

puts mojiretu1 == mojiretu2        #=> true
puts mojiretu1.equal?(mojiretu2)   #=> false
```

Symbolの場合、値もインスタンスも同じ。

```rb
symbol1 = :symbol
symbol2 = :symbol

puts symbol1 == symbol2        #=> true
puts symbol1.equal?(symbol2)   #=> true
```

### Symbol 使用上の注意
Symbol は _ 以外の記号は使えない

```rb
# これはエラーになる
#hash5 = { first-element: "sazae", second-element: "masuo" }

# _ (アンダーバー)だけ使える
hash5 = { :first_element  => "sazae", :second_element => "masuo" }
puts hash5[:first_element] # sazae と表示

# 文字列ハッシュキーが文字列の場合は - も使える
hash6 = { "first-element" => "sazae", "second-element" => "masuo" }
puts hash6["second-element"] # masuo と表示
```
参考/引用: [Hash から文字列でもシンボルでも値を取り出す](http://qiita.com/QUANON/items/169c73425a6bc50dee51)
参考/引用: [Rubyのややこしい配列とハッシュとシンボルについて整理してみた](http://blog.isao.co.jp/organize-array-and-hash-and-symbol-of-ruby/)
参考/引用: [Ruby 2.2 で {"Arbitrary Key": "value"} が出来るようになった話](http://sixeight.hatenablog.com/entry/2014/12/25/205411)
参考/引用: [Ruby のシンボル ](http://jutememo.blogspot.jp/2008/01/ruby.html)




## Hash と Array の繰り返し文を整理する
### (1) Array の繰り返し
```rb
puts "(1) Array の繰り返し"
array1 = [ "sazae", "masuo", "tarao" ]

#配列.each do |変数|
#  実行する処理1
#  実行する処理2
#end

array1.each do |namae|
  puts namae
end
```

### (2) Hash Symbol の繰り返し
```rb
puts "(2) Hash Symbol の繰り返し"
hash2 = { :first => "sazae", :second => "masuo", :third => "tarao" }

#連想配列.each do |キー, バリュー|
#  実行する処理1
#  実行する処理2
#end

hash2.each do |key, value|
   puts "key = #{key}"
   puts "value = #{value}"
end
```

### (3) Hash String の繰り返し
```rb
puts "(3) Hash String の繰り返し"
hash3 = {
  "1.1.1.1" => "example1.com",
  "2.2.2.2" => "example2.com",
  "3.3.3.3" => "example3.com"
}

hash3.each do |key, value|
   puts "key = #{key}"
   puts "value = #{value}"
end
```

### (4) Hash の Value を 更に Hash(String Key)にして繰り返し
```rb
puts "(4) Hash の Value を 更に Hash(String Key)にして繰り返し"
hash4 = {
  "1.1.1.1" => { "hostname" => "example1.com", "comment" => "hoge1" },
  "2.2.2.2" => { "hostname" => "example2.com", "comment" => "hoge2" },
  "3.3.3.3" => { "hostname" => "example3.com", "comment" => "hoge3" }
}

hash4.each do |key, value|
   puts "key = #{key}"
   puts "value = #{value["hostname"]}"
   puts "comment = #{value["comment"]}"
end
```

### (5) Hash の Value を 更に Hash(String Symbol)にして繰り返し
```rb
puts "(5) Hash の Value を 更に Hash(String Symbol)にして繰り返し"
hash5 = {
  "1.1.1.1" => { hostname: "example1.com", comment: "hoge1" },
  "2.2.2.2" => { hostname: "example2.com", comment: "hoge2" },
  "3.3.3.3" => { hostname: "example3.com", comment: "hoge3" }
}
 
hash5.each do |key, value|
   puts "key = #{key}"
   puts "value = #{value[:hostname]}"
   puts "comment = #{value[:comment]}"
end
```