Ruby基礎復習(6) Hash

『パーフェクトRuby』p.179より。

まず基本的なとこで。

 1hash = {hoge: 1, fuga: 2}
 2
 3hash.each do |key, val|
 4  p "#{key}: #{val}"
 5end # => "hoge: 1", "fuga: 2"
 6
 7hash.each_key do |key|
 8  p key
 9end # => "hoge", "fuga"
10
11hash.each_value do |val|
12  p val
13end # => "1", "2"
14
15hash[:hoge] = 3
16p hash # => {hoge: 3, fuga: 2}
17hash[:piyo] = 4
18p hash # => {hoge: 3, fuga: 2, piyo: 4}
19
20hash.delete(:piyo)
21hash # => {hoge: 3, fuga: 2}
22
23hash.empty? # => false
24hash.length # => 2

ハッシュの生成はHash[]により偶数個の引数から行うこともできる。

1ary = ["hoge", 1, "fuga", 2]
2Hash[*ary] # => {hoge: 1, fuga: 2}
3
4ary = [["hoge", 1], ["fuga", 2]]
5Hash[ary] # => {hoge: 1, fuga: 2}

Arrayクラスと同様の#select#reject#keep_if#delete_if操作が可能。

 1hash = {hoge: 1, fuga: 2, piyo: 3}
 2
 3hash.select {|key, val| val.even? } # => {fuga: 2}
 4p hash # => {hoge: 1, fuga: 2, piyo: 3}
 5hash.select! {|key, val| val.even? } # => {fuga: 2}
 6p hash # => {fuga: 2}
 7
 8hash = {hoge: 1, fuga: 2, piyo: 3}
 9
10hash.reject {|key, val| val.even? } # => {hoge: 1, piyo: 3}
11p hash # => {hoge: 1, fuga: 2, piyo: 3}
12hash.reject! {|key, val| val.even? } # => {hoge: 1, piyo: 3}
13p hash # => {hoge: 1, piyo: 3}
14
15hash.select! {|key, val| val.even? } # => nil
16hash.keep_if {|key, val| val.even? } # => {hoge: 1, piyo: 3}
17hash.reject! {|key, val| val.even? } # => nil
18hash.delete_if {|key, val| val.even? } # => {hoge: 1, piyo: 3}

Hashの統合はHash#mergeを用いる。キーが重複する場合は、引数で渡されたハッシュの値で上書きされる。ブロックを引き渡している場合は、キー重複時の処理をブロックの中で定義できる。破壊的操作であるHash#merge!Hash#updateとも書くことが出来る。

1a = {hoge: 1, fuga: 2}
2b = {hoge: 3, piyo: 4}
3a.merge(b) # => {hoge: 3, fuga: 2, piyo: 4}
4p a # => {hoge: 1, fuga: 2}
5
6a.merge!(b) {|key, a_val, b_val|
7  a_val + b_val
8} # => {hoge: 4, fuga: 2, piyo: 4}
9p a # => {hoge: 4, fuga: 2, piyo: 4}

キーと値の取得に関して。特に特定キーの存在確認については、Hash#has_key?を用いる。通常のHash[]による呼び出しだと、値が存在しない場合でもnilが返ってきてしまい、値がnilなのか、それとも存在していないのか区別がつかないため。あるいはHash#fetchを用いれば、値が存在しない場合の返り値を指定できる。

 1hash = {hoge: 1, fuga: 2, piyo: 3, hogehoge: nil}
 2
 3hash.keys # => [:hoge, :fuga, :piyo]
 4hash.key(2) # => :fuga
 5
 6hash.values # => [1, 2, 3]
 7hash.values_at(:fuga) # => [2]
 8hash.values_at(:fuga, :piyo) # => [2, 3]
 9
10hash[:hogehoge] # => nil
11hash[:foo] # => nil
12hash.has_key?(:foo) # => false
13# 以下すべてhas_key?と同義
14hash.member?(:foo)
15hash.include?(:foo)
16hash.key?(:foo)
17
18hash.fetch(:foo) # => nil
19hash.fetch(:foo, "error") # => "error"
20hash.fetch(:foo){|key| "#{key} not exists"} # => "foo not exists"
21
22hash.has_value?(3) # => true
23hash.value?(3) # => true

Hashにはデフォルト値の概念があり、Hash#newの引数に与えた値が、存在しないキーを参照したときの返り値となる(デフォルトはnil)。ここで指定した値はすべて同一オブジェクトであり、破壊的操作をする場合などは注意が必要。またHash#default=Hash#default_proc=により、既存のHashオブジェクトに対してもデフォルト値の変更が可能。

 1hash = Hash.new("null")
 2hash[:foo] # => "null"
 3
 4hash.default = "undefined"
 5hash[:foo] # => "undefined"
 6default = hash.default
 7default.reverse!
 8hash[:foo] # => "denifednu"
 9
10hash.default_proc = ->(hash, key) {"Key: #{key} not exists"}
11hash[:foo] # => "Key: foo not exists"

ハッシュ変換系のメソッド。

1hash = {hoge: 1, fuga: 2}
2
3hash.invert # => {1: hoge, 2: fuga}
4hash.to_a # => [[:hoge, 1], [:fuga, 2]]
5hash.sort # => [[:fuga, 2], [:hoge, 1]]