Reputation: 71
I have a hash which is say,
hash = {"lock_version"=>4,
"exhibition_quality"=>false,
"within"=>["FID6", "S2"],
"repository"=>
{"ref"=>"/repositories/2",
"repository"=>{"ref"=>"/repositories/2",
"within"=>["FID6", "S2"]
}
}
This hash is been passed through another function. How can I delete from "within"=>["FID6", "S5"] a value with the pattern FID (in this example FID6) without mutating the original hash ash well? This is just a shortened version of the hash but there are other instances where the hash is super long and "within" key value pair appears multiple times. Note: This program is using ruby 2.4
I have been asked to clarify how this question is different from a previous question I asked so this is a little bit of more clarification because I've done more work on it since. This specific key value pair "within"=>["FID6", "S2"], is now appearing deeply nested (the entire hash is about 2 pages long, hence why I didn't copy and paste it). I can't split the hash where the "repository" is because it appears nested in other key values. What I'm asking now is just is there a way to match that within key value no matter now deep it. Thanks everyone for the suggestions.
Upvotes: 1
Views: 434
Reputation: 110645
Code
def defidder(h)
h.each_with_object({}) do |(k,v),h|
h[k] =
case v
when Array
v.reject { |s| s.match?(/\AFID\d+\z/) } if k == "within"
when Hash
defidder(v)
else
v
end
end
end
Example
I've added another layer of hash nesting to the example given in the question:
hash = {
"lock_version"=>4,
"exhibition_quality"=>false,
"within"=>["FID6", "S2"],
"repository"=>{
"ref"=>"/repositories/2",
"repository"=>{"ref"=>"/repositories/2"},
"within"=>["FID6", "S2"],
"1more"=>{ a: 1, "within"=>["FID999", "S7"] }
}
}
defidder hash
#=> {
# "lock_version"=>4,
# "exhibition_quality"=>false, "within"=>["S2"],
# "repository"=>{
# "ref"=>"/repositories/2",
# "repository"=>{"ref"=>"/repositories/2"},
# "within"=>["S2"],
# "1more"=>{:a=>1, "within"=>["S7"]
# }
# }
We may verify hash
was not mutated.
hash
#=> {
# "lock_version"=>4,
# "exhibition_quality"=>false,
# "within"=>["FID6", "S2"],
# "repository"=>{
# "ref"=>"/repositories/2",
# "repository"=>{"ref"=>"/repositories/2"},
# "within"=>["FID6", "S2"],
# "1more"=>{ a: 1, "within"=>["FID999", "S7"] }
# }
# }
Upvotes: 1
Reputation: 2318
Assuming:
This works with your example and works with examples I created with the assumptions above:
cloned_hash = Marshal.load(Marshal.dump(hash))
def remove_key_value_pair(key, value, hash)
if hash.key?(key) && hash[key] == value
hash.delete(key)
end
hash.each{|k, v| remove_key_value_pair(key, value, v) if v.is_a? Hash }
end
# call with
remove_key_value_pair("within", ["FID6", "S2"], cloned_hash)
This will run into a SystemStackError
if the hash has a lot of nesting.
Upvotes: 0