🙄

Ruby の #dig で任意のオブジェクトでも参照できるようにする

に公開

Hash#digArray#dig は『ネストしたデータを掘り下げて値を取得する』ことができます。

data = { data: { names: ["homu", "mami", "mado"] } }

# data[:data][:names][1] と同じようにアクセスできる
pp data.dig(:data, :names, 1)
# => "name"

data[:data][:names][1] と違って何がよいのかというと『途中で存在しない要素があってもエラーにならない』という利点があります。

data = { data: { names: ["homu", "mami", "mado"] } }

# dig の場合はエラーにならず nil を返す
pp data.dig(:data, :name, 1)
# => nil

# data[:data][:name] が nil を返すので nil[1] でアクセスしようとしてエラーになる
data[:data][:name][1]
# => error: undefined method '[]' for nil (NoMethodError)

他のオブジェクトでも #dig で掘り下げられるようにする

この #dig メソッドなのですが『対象のオブジェクトの #dig メソッドを再帰的に呼び出す』みたな仕組みになっています。
逆に #dig メソッドがない場合はエラーになります。

user = { name: "honu" }

pp user.dig(:name, 1)
# => error: String does not have #dig method (TypeError)

逆に対象にしたいオブジェクトに #dig メソッドを定義しておけば他の #dig から呼び出すことができます。

class String
  # String に #dig メソッドを定義しておく
  alias_method :dig, :[]
end

user = { name: "honu" }

pp user.dig(:name, 1)
# => "o"

ActiveRecord のモデルクラスも #dig は使えないんですが、次のように #dig メソッドを定義しておけば他の #dig から利用できるようになるので便利かもしれませんね。

class User < ActiveRecord::Base
  alias_method :dig, :[]
end

data = { user: User.create(name: "homu", age: 14) }

pp data.dig(:user, :name)
# => "homu"
GitHubで編集を提案

Discussion