Question

I am now trying for some hours to remove a nested hash key of a hash list. I saw many solution non-nested hashs wich looks like this:

   sample_hash = {"key1" => "value1", "key2" => "value2"}
   sample_hash.except("key1") 

This results in:

  {"key2"=>"value2"}

But if I try to use the except method on a hash with nested key then it doesn't work. Here my code:

  nested_hash = {"key1"=>"value1", "key2"=>{
                                           "nested_key1"=>"nestedvalue1",
                                           "nested_key2"=>"nestedvalue2"
                                           }
                }

  nested_hash.except("nested_key2")

The except() method returns the nested_hash without any changes. I have looked for a solution how I can pass nested hash-keys to the except method, but couldn't find anything. Is it even possible to pass nested keys to this method or should I use some other method which deletes a nested hash key from my hash list?

Was it helpful?

Solution

what about

Hash[nested_hash.map {|k,v| [k,(v.respond_to?(:except)?v.except("nested_key2"):v)] }]

=> {"key1"=>"value1", "key2"=>{"nested_key1"=>"nestedvalue1"}}

ugh.

OTHER TIPS

The accepted solution is valid for the scenario given but if you're looking for something that will do this for arbitrarily nested hash tables then you're going to need a recursive solution. I couldn't find a suitable solution anywhere, so I wrote one here.

Reproduced here with annotations:

class Hash
  def except_nested(key)
    r = Marshal.load(Marshal.dump(self)) # deep copy the hashtable
    r.except_nested!(key)
  end

  def except_nested!(key)
    self.except!(key)
    self.each do |_, v| # essentially dfs traversal calling except!
      v.except_nested!(key) if v.is_a?(Hash)
    end
  end
end

adding it to the Hash class so that you can call it the same way you call except/except! anywhere else.

t = { a: '1', b: { c: '3', d: '4' } } 

r = t.except_nested(:c) 
# r => {:a=>"1", :b=>{:d=>"4"}}
# t => {:a=>"1", :b=>{:c=>"3", :d=>"4"}}

t.except_nested!(:c)
# t => {:a=>"1", :b=>{:d=>"4"}}

try

my_hash = Hash[nested_hash.map {|k,v| {k=>v.is_a? Array ? v.except("nested_key2") : v}}.map {|key, value| [key, value]}]

But this seems wrong, I wish I never started down this path, I'm willing to bet there is an easier way!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top