読者です 読者をやめる 読者になる 読者になる

tech-dig

新しい技術が見つかるブログ

Ruby における hash 操作の逆引きまとめ

Ruby の hash にまつわる操作でよく使われるメソッドをまとめました。
用途別にグループ分けをしているので、逆引きとしてご覧ください。

※ 想定している ruby のバージョンは ruby 2.3.1p112 です。

要素を追加する

ハッシュに要素(キーと値のペア)を追加したい時は、下記のようなやり方があります。

一つずつ追加する

[]=

単純な要素追加の場合には []= を使うのが一般的です。

hash = {}
hash[:hoge] = 1

hash
# {:hoge=>1}

store

[]= の別名のメソッドとして store も使うことができます。

hash = {}
hash.store(:fuga, 2)

hash
# {:fuga=>2}

ハッシュに別のハッシュを加える

merge, merge!

merge を使うと、すでに存在するハッシュの要素をもう一つのハッシュに統合できます。 同じキーが存在した場合は、引数で与えたハッシュの値で上書きされます。

hash1 = { a: 1, b: 1 }
# {:a=>1, :b=>1}
hash2 = { b: 2, c: 2 }
# {:b=>2, :c=>2}

hash1.merge(hash2)
# {:a=>1, :b=>2, :c=>2}

merge! の場合はレシーバ自身が上書きされます。

## merge はレシーバ自身は変更しない
hash1.merge(hash2)
# {:a=>1, :b=>2, :c=>2}
hash1
# {:a=>1, :b=>1}

## merge! はレシーバ自身が変更される
hash1.merge!(hash2)
# {:a=>1, :b=>2, :c=>2}
hash1
# {:a=>1, :b=>2, :c=>2}

update

merge! の別名のメソッドとして update があります。

hash = { hoge: 1 }
# {:hoge=>1}

hash.update({ fuga: 2})
# {:hoge=>1, :fuga=>2}

hash
# {:hoge=>1, :fuga=>2}

値を検索する

特定の値が存在するか調べる

value?

特定の値が存在するかどうかを判定し、Boolean で返します。

hash = { name: 'Tom', age: 21 }
# {:name=>"Tom", :age=>21}

hash.value?('Tom')
# true
hash.value?('Bob')
# false

has_value?

value? の同名のメソッドとして has_value? があります。

hash = { name: 'Tom', city: 'Tokyo' }
# {:name=>"Tom", :city=>"Tokyo"}

hash.has_value?('Tokyo')
# true
hash.has_value?('Osaka')
# false

値の一覧を返す

values

ハッシュの中に値として存在しているものを配列で返します。

hash = { a: 1, b: 2, c: 3 }
# {:a=>1, :b=>2, :c=>3}

hash.values
# [1, 2, 3]

特定のキー(単一)に紐づく値を取り出す

[]

あるキーに紐づく値を参照する場合には [] を用いるのが一般的です。

hash = { hoge: 1 }
# {:hoge=>1}

hash[:hoge]
# 1

fetch

[] と同様に値を取り出すことができますが、該当のキーが存在しない場合に KeyError を返します。

hash = { hoge: 1 }

hash.fetch(:hoge)
# 1
hash.fetch(:fuga)
# KeyError: key not found: :fuga

第2引数を渡すことで、キーが存在しなかった場合のデフォルト値を設定することができます。

hash.fetch(:fuga)
# KeyError: key not found: :fuga

hash.fetch(:fuga, "NotFound")
# "NotFound"

特定のキー(複数可)が持つ値を配列で返す

fetch_values

引数としてキーを可変長な引数として渡すことで、それらのキーが持つ値を配列で取得できます。

hash = { a: 1, b: 2, c: 3 }
# {:a=>1, :b=>2, :c=>3}

hash.fetch_values(:a, :c)
# [1, 3]

ネストした要素を参照する

dig

ruby 2.3 で追加された dig メソッドを使えば、ネストした要素を安全に取得できます。

hash = { a: { b: { c: 3 } } }
# {:a=>{:b=>{:c=>3}}}

hash[:a][:b][:c]
# 3
hash.dig(:a, :b, :c)
# 3

## []のチェーンだと、途中でキーがない場合にエラーになる
hash[:a][:not_b][:c]
# NoMethodError: undefined method `[]' for nil:NilClass

## dig の場合は途中でキーがない場合はエラーにならず nil が返る
hash.dig(:a, :not_b, :c)
# nil

特定の値(単一)に紐づく値を取り出す

特定の値を持つキーが存在する場合はそのキー名を返し、存在しない場合は nil を返します。

key

hash = { name: 'Tom', city: 'Tokyo' }
# {:name=>"Tom", :city=>"Tokyo"}

hash.key('Tokyo')
# :city
hash.key('Osaka')
# nil

キーを検索する

特定のキーが存在するか調べる

key?

特定のキーが存在するかどうかを判定し、Boolean で返します。

hash = { a: 1, b: 2, c: 3 }
# {:a=>1, :b=>2, :c=>3}

hash.key?(:b)
# true
hash.key?(:d)
# false

has_key?

key? の同名のメソッドとして has_key? があります。

hash = { a: 1, b: 2, c: 3 }
# {:a=>1, :b=>2, :c=>3}

hash.has_key?(:b)
# true
hash.has_key?(:d)
# false

キーの一覧を返す

keys

ハッシュの中に存在するキーを配列にして返します。

hash = { name: 'Tom', city: 'Tokyo' }
# {:name=>"Tom", :city=>"Tokyo"}

hash.keys
# [:name, :city]

繰り返し処理する

キーごとに処理をする

each_key

ハッシュ内に存在するキーをブロック変数にしてイテレーションを回します。

hash = { name: 'Tom', city: 'Tokyo', age: 21 }
# {:name=>"Tom", :city=>"Tokyo", :age=>21}

hash.each_key { |key| puts key }
# name
# city
# age

値ごとに処理をする

each_value

ハッシュ内に存在する値をブロック変数にしてイテレーションを回します。

hash = { name: 'Tom', city: 'Tokyo', age: 21 }
# {:name=>"Tom", :city=>"Tokyo", :age=>21}

hash.each_value { |value| puts value }
# Tom
# Tokyo
# 21

キーと値のペアで処理をする

each_pair

ハッシュ内に存在するキーと値の2つをブロック変数にしてイテレーションを回します。

hash = { name: 'Tom', city: 'Tokyo', age: 21 }
# {:name=>"Tom", :city=>"Tokyo", :age=>21}

hash.each_pair { |key, value| puts "key: #{key}, value :#{value}" }
# key: name, value: Tom
# key: city, value: Tokyo
# key: age, value: 21

その他の操作

キーと値を入れ替える

invert

ハッシュのキーと値を入れ替えたハッシュを生成します。
キーは一意性を担保する必要があるため、値が重複している状態で invert した場合は後ろのキーが残ります。
(Ruby 1.9 以降はキーにも順序が存在します)

hash = { A: "Tom", B: "Bob", C: "Green" }
# {:A=>"Tom", :B=>"Bob", :C=>"Green"}

hash.invert
# {"Tom"=>:A, "Bob"=>:B, "Green"=>:C}

## 同じ値がある状態で invert した場合は後ろのキーが採用される
price = { orange: 150, apple: 200, banana: 150 }
# {:orange=>150, :apple=>200, :banana=>150}
price.invert
# {150=>:banana, 200=>:apple}

特定の条件を満たす要素を削除する

delete

引数として渡したキーの要素を削除します。
レシーバは変更されたうえで、戻り値としては削除された要素の値が返ります。

hash = { A: "Tom", B: "Bob", C: "Green" }
# {:A=>"Tom", :B=>"Bob", :C=>"Green"}

hash.delete(:A)
# "Tom"
hash
# {"Bob"=>:B, "Green"=>:C}

delete_if

キーと値を引数にした繰り返しブロックを実行し、真となる要素のみを削除します。

price = { orange: 150, apple: 200, banana: 80 }
# {:orange=>150, :apple=>200, :banana=>80}
price.delete_if { |_, price| price > 100 }
# {:banana=>80}

price
# {:banana=>80}

keep_if

delete_if とは逆に、繰り返しブロックを実行して真となった要素のみを残します。

price = { orange: 150, apple: 200, banana: 80 }
# {:orange=>150, :apple=>200, :banana=>80}

price.keep_if { |_, price| price > 100 }
# {:orange=>150, :apple=>200}