Edited at

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

More than 1 year has passed since last update.


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

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

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


Array(配列) とは

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

# 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が子供であることがコードを呼んでいる人に伝わりますよね。

# 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は {}で囲みます。大切です。

# 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です。

# 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の紐付けに => (ハッシュロケット)を使わず以下のようなシンプルな表記が可能になります。

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

# 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? はインスタンスが同じ場合に真となる。


試してみる。

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

mojiretu1 = "mojiretu"

mojiretu2 = "mojiretu"

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

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

symbol1 = :symbol

symbol2 = :symbol

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


Symbol 使用上の注意

Symbol は _ 以外の記号は使えない

# これはエラーになる

#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 から文字列でもシンボルでも値を取り出す

参考/引用: Rubyのややこしい配列とハッシュとシンボルについて整理してみた

参考/引用: Ruby 2.2 で {"Arbitrary Key": "value"} が出来るようになった話

参考/引用: Ruby のシンボル


Hash と Array の繰り返し文を整理する


(1) Array の繰り返し

puts "(1) Array の繰り返し"

array1 = [ "sazae", "masuo", "tarao" ]

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

array1.each do |namae|
puts namae
end


(2) Hash Symbol の繰り返し

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 の繰り返し

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)にして繰り返し

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)にして繰り返し

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