james james - 6 months ago 13
Ruby Question

How to recursively check for presence of specific key

Given a Ruby hash of parameters that are infinitely nested, I want to write a function that returns

true
if a given key is in those parameters.

This is the function I have so far, but it's not quite right, and I'm at a loss as to why:

def has_key(hash, key)
hash.each do |k, v|
if k == key
return true
elsif v.class.to_s == "Array"
v.each do |inner_hash|
return has_key(inner_hash,key)
end
else
return false
end
end
end


The method should return the following results:

# all check for presence of "refund" key

has_key({
"refund" => "2"
}, "refund")
=> true

has_key({
"whatever" => "3"
}, "refund")
=> false

has_key({
"whatever" => "3",
"child_attributes" => [{
"refund" => "1"
}]
}, "refund")
=> true

has_key({
"whatever" => "3",
"child_attributes" => [{
"nope" => "4"
}]
}, "refund")
=> false

has_key({
"whatever" => "3",
"child_attributes" => [{
"a" => "1",
"refund" => "2"
}]
}, "refund")
=> true

has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"aa" => "1", "refund" => "2"}
]
}, "refund")
=> true

has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}
]}
]
}, "refund")
=> false

has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3"}, {"refund" => "5"}
]}
]
}, "refund")
=> true

has_key({
"whatever" => "3",
"child_attributes" => [
{"a" => "1", "b" => "2"},
{"grand_child_attributes" => [
{"test" => "3", "refund" => "5"}
]}
]
}, "refund")
=> true

Answer

You could convert the hash to JSON and then check whether the JSON has "refund": present in it, as any key will be serialised to JSON in the form "key":.

require "json"
hash.to_json.include?('"refund":')