Rubyで多次元HashやArrayから特定のキーを削除する
一次元のHashから特定のキーを削除したい場合は、
h = {a: 1, b: 2, c: 3}
# :bを削除
h.delete_if{|k, v| k == :b }
=>{a: 1, c: 3}
が1つの手ですが、多次元Hashの場合はどうするか。
h = {a: {aa: 1, ab: 2, ac: 3}, b: 2, c:3}
# :aaを削除したい
# NG
h.delete_if{|k, v| k == :aa }
=> {a: {aa: 1, ab: 2, ac: 3}, b: 2, c:3}
# OK
p = proc do |k, v|
v.respond_to?(:delete_if) ? (v.delete_if(&p); nil) : k == :aa
end
h.delete_if(&p)
=> {a: {ab: 2, ac: 3}, b: 2, c:3}
条件をprocにして再帰的にdelete_ifをします。
v.respond_to?(:delete_if)
部分は、v.instance_of?(Hash)
でもいいかもしれません。どれだけ階層が深くなっても対応できるはず。
ただ、HashにArrayも入ってくると上記のコードだけではまかなえないため、以下のようなコードとしました。
h = {a: [{aa: 1, ab: 2, ac: 3}, {aa: 4, ab: 5, ac: 6}], b: 2, c:3}
p = proc do |k, v|
k.respond_to?(:delete_if) ? (k.delete_if(&p); nil) : (v.respond_to?(:delete_if) ? (v.delete_if(&p); nil) : k == :aa)
end
h.delete_if(&p)
=> {a: [{ab: 2, ac: 3}, {ab: 5, ac: 6}], b: 2, c: 3}
key
とval
それぞれにdelete_if
の存在を確認しつつ、純粋なHashであればkeyを評価する処理です。
ただ、ちょっと長ったらしい感じなので、再帰の条件がもっとシンプルにならないものかなあとは思います。