Hannah Baker
Hannah Baker

Reputation: 71

How would I remove multiple nested values from a hash

I've got a really confusing question that I can't seem to figure out.

Say I have this hash

hash = {
 "lock_version"=>1, 
 "exhibition_quality"=>false,
 "within"=>["FID6", "S5"], 
 "representation_file"=>{
    "lock_version"=>0, 
    "created_by"=>"admin", 
    "within"=>["FID6", "S5"]
  }
}

How can I delete from "within"=>["FID6", "S5"] a value with the pattern FID<Number> (in this example FID6)?

I've thought about it a bit and used the .delete, .reject! the within but I realised this was deleting the whole key value pair which is not what I want. Thanks a lot for any help that can put me on the right path.

Upvotes: 0

Views: 490

Answers (2)

3limin4t0r
3limin4t0r

Reputation: 21110

You could use a method or proc/lambda to achieve the result. This solutions splits up the logic in two parts. Removing the actual FID<Number> string from the array and recursively calling the former on the correct key.

remove_fid = ->(array) { array.grep_v(/\AFID\d+\z/) }

remove_nested_fid = lambda do |hash|
  hash.merge(
    hash.slice('within').transform_values(&remove_fid),
    hash.slice('representation_file').transform_values(&remove_nested_fid)
  )
end

pp hash.then(&remove_nested_fid) # or remove_nested_fid.call(hash)
# {"lock_version"=>1,
#  "exhibition_quality"=>false,
#  "within"=>["S5"],
#  "representation_file"=>
#   {"lock_version"=>0, "created_by"=>"admin", "within"=>["S5"]}}

grep_v removes all strings from the array that do not match the given regex.

slice creates a new hash only containing the given keys. If a key is missing it will not be present in the resulting hash.

transform_values transforms the values of a hash into a new value (similar to map for Array), returning a hash.

merge creates a new hash, merging the hashes together.

This solution does not mutate the original hash structure.

Upvotes: 1

asthasr
asthasr

Reputation: 9397

You're going to need to recurse over the Hash and, in cases where the value is a Hash, process it using the same function, repeatedly. This can be problematic in Ruby -- which doesn't handle recursion very well -- if the depth of the tree is too deep, but it's the most natural way to express this type of issue.

def filter_fids(h)
    h.each_pair do |k, v|
        if v.is_a? Hash
            filter_fids v
        elsif k == "within"
            v.reject! { |x| x.start_with? "FID" }
        end
    end
end

This will mutate the original data structure in place.

Upvotes: 0

Related Questions