chroju.dev/blog

the world as code

Ruby基礎復習(6) Hash

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

まず基本的なとこで。

hash = {hoge: 1, fuga: 2}

hash.each do |key, val|
  p "#{key}: #{val}"
end # => "hoge: 1", "fuga: 2"

hash.each_key do |key|
  p key
end # => "hoge", "fuga"

hash.each_value do |val|
  p val
end # => "1", "2"

hash[:hoge] = 3
p hash # => {hoge: 3, fuga: 2}
hash[:piyo] = 4
p hash # => {hoge: 3, fuga: 2, piyo: 4}

hash.delete(:piyo)
hash # => {hoge: 3, fuga: 2}

hash.empty? # => false
hash.length # => 2

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

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

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

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

hash = {hoge: 1, fuga: 2, piyo: 3}

hash.select {|key, val| val.even? } # => {fuga: 2}
p hash # => {hoge: 1, fuga: 2, piyo: 3}
hash.select! {|key, val| val.even? } # => {fuga: 2}
p hash # => {fuga: 2}

hash = {hoge: 1, fuga: 2, piyo: 3}

hash.reject {|key, val| val.even? } # => {hoge: 1, piyo: 3}
p hash # => {hoge: 1, fuga: 2, piyo: 3}
hash.reject! {|key, val| val.even? } # => {hoge: 1, piyo: 3}
p hash # => {hoge: 1, piyo: 3}

hash.select! {|key, val| val.even? } # => nil
hash.keep_if {|key, val| val.even? } # => {hoge: 1, piyo: 3}
hash.reject! {|key, val| val.even? } # => nil
hash.delete_if {|key, val| val.even? } # => {hoge: 1, piyo: 3}

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

a = {hoge: 1, fuga: 2}
b = {hoge: 3, piyo: 4}
a.merge(b) # => {hoge: 3, fuga: 2, piyo: 4}
p a # => {hoge: 1, fuga: 2}

a.merge!(b) {|key, a_val, b_val|
  a_val + b_val
} # => {hoge: 4, fuga: 2, piyo: 4}
p a # => {hoge: 4, fuga: 2, piyo: 4}

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

hash = {hoge: 1, fuga: 2, piyo: 3, hogehoge: nil}

hash.keys # => [:hoge, :fuga, :piyo]
hash.key(2) # => :fuga

hash.values # => [1, 2, 3]
hash.values_at(:fuga) # => [2]
hash.values_at(:fuga, :piyo) # => [2, 3]

hash[:hogehoge] # => nil
hash[:foo] # => nil
hash.has_key?(:foo) # => false
# 以下すべてhas_key?と同義
hash.member?(:foo)
hash.include?(:foo)
hash.key?(:foo)

hash.fetch(:foo) # => nil
hash.fetch(:foo, "error") # => "error"
hash.fetch(:foo){|key| "#{key} not exists"} # => "foo not exists"

hash.has_value?(3) # => true
hash.value?(3) # => true

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

hash = Hash.new("null")
hash[:foo] # => "null"

hash.default = "undefined"
hash[:foo] # => "undefined"
default = hash.default
default.reverse!
hash[:foo] # => "denifednu"

hash.default_proc = ->(hash, key) {"Key: #{key} not exists"}
hash[:foo] # => "Key: foo not exists"

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

hash = {hoge: 1, fuga: 2}

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