MxLDevs
MxLDevs

Reputation: 19506

Ruby: Deleting all instances of a particular key from hash of hashes

I have a hash like

h = {1 => {"inner" => 45}, 2 => {"inner" => 46}, "inner" => 47}

How do I delete every pair that contains the key "inner"?
You can see that some of the "inner" pairs appear directly in h while others appear in pairs in h

Note that I only want to delete the "inner" pairs, so if I call my mass delete method on the above hash, I should get

h = {1 => {}, 2 => {}}

Since these pairs don't have a key == "inner"

Upvotes: 10

Views: 4484

Answers (5)

earlonrails
earlonrails

Reputation: 5182

Similar answer but it is a whitelist type approach. For ruby 1.9+

# recursive remove keys
def deep_simplify_record(hash, keep)
  hash.keep_if do |key, value|
    if keep.include?(key) 
      deep_simplify_record(value, keep) if value.is_a?(Hash)
      true
    end
  end
end

hash = {:a => 1, :b => 2, :c => {:a => 1, :b => 2, :c => {:a => 1, :b => 2, :c => 4}} }
deep_simplify_record(hash, [:b, :c])
# => {:b=>2, :c=>{:b=>2, :c=>{:b=>2, :c=>4}}}

Also here are some other methods which I like to use for hashes. https://gist.github.com/earlonrails/2048705

Upvotes: 0

def except_nested(x,key)
  case x
  when Hash then x = x.inject({}) {|m, (k, v)| m[k] = except_nested(v,key) unless k == key ; m }
  when Array then x.map! {|e| except_nested(e,key)}
  end
  x
end

Upvotes: 9

John Naegle
John Naegle

Reputation: 8247

Here is what I came up with:

class Hash
  def deep_reject_key!(key)
    keys.each {|k| delete(k) if k == key || self[k] == self[key] }

    values.each {|v| v.deep_reject_key!(key) if v.is_a? Hash }
    self
  end
end

Works for a Hash or a HashWithIndifferentAccess

> x = {'1' => 'cat', '2' => { '1' => 'dog', '2' => 'elephant' }}
=> {"1"=>"cat", "2"=>{"1"=>"dog", "2"=>"elephant"}}

> y = x.with_indifferent_access
=> {"1"=>"cat", "2"=>{"1"=>"dog", "2"=>"elephant"}}

> x.deep_reject_key!(:"1")
=> {"1"=>"cat", "2"=>{"1"=>"dog", "2"=>"elephant"}}

> x.deep_reject_key!("1")
=> {"2"=>{"2"=>"elephant"}}

> y.deep_reject_key!(:"1")
=> {"2"=>{"2"=>"elephant"}}

Upvotes: 3

pguardiario
pguardiario

Reputation: 54984

Really, this is what reject! is for:

def f! x
  x.reject!{|k,v| 'inner' == k} if x.is_a? Hash
  x.each{|k,v| f! x[k]}
end

Upvotes: 10

DigitalRoss
DigitalRoss

Reputation: 146053

def f x 
  x.inject({}) do |m, (k, v)|
    v = f v if v.is_a? Hash  # note, arbitrarily recursive
    m[k] = v unless k == 'inner'
    m
  end
end

p f h

Update: slightly improved...

def f x
  x.is_a?(Hash) ? x.inject({}) do |m, (k, v)|
    m[k] = f v unless k == 'inner'
    m
  end : x
end

Upvotes: 8

Related Questions