CraigD
CraigD

Reputation: 25

Is there a way to do a deep delete using the ruby hashie gem?

So hashie has a deep_find_all method that one can use to find all of the keys in a nested hash, but is there a way to do a deep delete or a deep find that returns the keys in a manner that they can be deleted.

I see where I can convert a hash into a mashie and do a delete but it doesn't seem to go to beyond the top level.

I am attempting to delete all the keys from JSON that contain a timestamp so that I can compare the results from two different hosts. I've implemented a way to do it, but it seems kind of clumsy and a deep delete would be just the ticket.

Thanks Craig

Upvotes: 1

Views: 161

Answers (1)

max pleaner
max pleaner

Reputation: 26788

You can make a generic ruby method:

def hash_recurse(hash, &blk)
  hash.each do |key, val|
    blk.call(hash, key, val)
    if val.is_a?(Hash)
      hash_recurse(val, &blk)
    end
  end
end

Then you can pass any block, including one to delete:

hash = {
  a: {
    timestamp: 123,
    key: "val1"
  },
  b: {
    timestamp: 456,
    foo: "val2"
  }
}

hash_recurse(hash) do |_hash, key, val|
  _hash.delete(key) if key == :timestamp
end

puts hash
# => {:a=>{:key=>"val1"}, :b=>{:foo=>"val2"}}

This is essentially the same thing as a depth-first search. For example, if you wanted to extract the 'paths' to all of the keys in the hash and it's subhashes, you could modify it to keep a record of the current path:

def hash_recurse(hash, path=[], &blk)
  hash.each do |key, val|
    new_path = path + [key]
    blk.call(hash, key, val, new_path)
    if val.is_a?(Hash)
      hash_recurse(val, new_path, &blk)
    end
  end
end

and fetch the paths like so:

paths = []
hash_recurse(hash) do |_hash, key, val, path|
  paths << path
end

paths.each { |path| puts path.join(",") }
# =>
#    a
#    a,timestamp
#    a,key
#    b
#    b,timestamp
#    b,foo

Upvotes: 1

Related Questions