Ruby

Ruby の Hash#fetch が便利

More than 1 year has passed since last update.

Hash#fetch は便利なメソッドです。基本的には Hash#[] と同じ動作をしますが、キーが存在しない場合の動作が異なります。

key が存在しない場合は例外が発生する

Hash#[] では指定した key が存在しない場合は nil を返します。

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user[:name]  #=> "Yamada Tarou"
p user[:email] #=> nil

しかし、Hash#fetch メソッドでは指定した key が存在しない場合は例外が発生します。これは Hash.new(0) のようにハッシュにデフォルト値を設定していても発生します。

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user.fetch(:name)  #=> "Yamada Tarou"
p user.fetch(:email) #=> `fetch': key not found: :email (KeyError)

例外が発生するため、key のスペルミスによるバグが発生しなくなります。

デフォルト値を指定することが可能

Hash#fetch は第二引数にデフォルト値を指定することが可能で、key が存在しない場合はこのデフォルト値を返します。

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user.fetch(:name)                       #=> "Yamada Tarou"
p user.fetch(:email, "dummy@example.com") #=> "dummy@example.com"

Hash#[] でも似たような操作を書くことは可能ですが

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user[:admin] || true     #=> true
p user.fetch(:admin, true) #=> true

指定した key の値が nilfalse の場合に期待した動作にならない場合があるので注意が必要です。

user = {
  id: 1,
  name: "Yamada Tarou",
  admin: false
}

p user[:admin] || true     #=> true
p user.fetch(:admin, true) #=> false

ブロックを受け取れる

また、Hash#fetch ではブロックを渡すことも可能で、キーが存在しない場合はそのブロックの戻り値を返します。ブロック引数には指定した key の値が入ります。

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user.fetch(:name)                       #=> "Yamada Tarou"
p user.fetch(:email) { |key|
  "#{key} not found"                      #=> "email not found"
}

これは第二引数に Proc オブジェクトを渡すことで同様のことが可能です。

default = -> (key) {
  "#{key} not found"
}

user = {
  id: 1,
  name: "Yamada Tarou"
}

p user.fetch(:name)            #=> "Yamada Tarou"
p user.fetch(:email, &default) #=> "email not found"

参考サイト

https://docs.ruby-lang.org/ja/latest/method/Hash/i/fetch.html