ネストされたHashをどんどん掘っていく、探査するメソッドが欲しいです。
が、Ruby 2.3からHash#digメソッドが追加されたので本記事は意味を成しません。
Ruby 2.2以前の時に参考になるレベルです。
# Hash掘るよ
# @param [Hash] data 対象のHash
# @param [Array] keys dataを掘っていくためのキー
# @return [Anything] 掘っていった結果
def dig(data, keys)
return data unless data.is_a?(Hash)
key = keys.shift
return data unless key
dig(data[key], keys)
end
data = {fuga: {bar: {piyo: 'good'}}}
p hoge(data, [:fuga, :bar, :piyo]) #=> 'good'
p hoge(data, [:fuga, :bar]) #=> {piyo: 'good'}
p hoge(data, [:fuga, :baz]) #=> nil
こんなの書いて使ってたけど、なんかダサい気がしてならない。
何かスマートな方法ないかと探してたらこんなものがありました。
ruby hash dig
転載させていただきますが、なにか問題あったら消しますので連絡ください。
Please contact me if there is a problem.
class Hash
def dig(*path)
path.inject(self) do |location, key|
location.is_a?(Hash) ? location[key] : nil
end
end
end
なるほど、injectの初期値に対象のデータをセットする発想はなかった!
injectはいつも空ハッシュにデータを突っ込んでいく使い方しかしていなかったので、このような使い方があるとは眼から鱗です。
でも個人的にオープンクラスが好きではないのと、デフォルト値を返すみたいなことができると嬉しいな、と思って改変しました。
# Hash形式のデータを掘る
# @param [Hash] data 掘る対象のデータ
# @param [Array] keys 掘りたいキー群
# @param [Anything] defalut キーがない場合、dataがHashじゃなくなった場合の返り値
# @return [Anything] 末端まで掘った値
def dig(data, keys, default={})
keys.inject(data) do |location, key|
(location.is_a?(Hash) && location[key]) ? location[key] : default
end
end
dig(data, [:fuga, :bar, :piyo]) #=> 'good'
dig(data, [:fuga, :bar]) #=> {piyo: 'good'}
dig(data, [:fuga, :baz]) #=> {}
dig(data, [:fuga, :baz], 'default') #=> 'default'
悪くないですね(自己満足)
結局、今回もこのメソッドを実装した後に仕様が変わって不要になりましたが、今後どこかで使うことも出てくると思ったのでQiitaに残しておきます。