How to recursively delete all keys with empty values ​​from the (YAML) hash?

I am trying to get rid of all hash keys in my YAML file that have empty (empty) values ​​or empty hashes as values.

This earlier post helped me get it almost right, but the recursive single-line layer leaves my YAML dump with empty hashes when there is enough deep nesting.

I would really appreciate any help on this. Thanks!

proc = Proc.new { |k, v| (v.kind_of?(Hash) && !v.empty? ) ? (v.delete_if(&proc); nil) : v.blank? } hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'} hash.delete_if(&proc) 

Actual output

  {"x"=>{"m"=>{}}, "y"=>"content"} 

Required conclusion

 {"y"=>"content"} 
+8
ruby ruby-on-rails yaml hash
source share
6 answers
 class Hash def delete_blank delete_if{|k, v| v.empty? or v.instance_of?(Hash) && v.delete_blank.empty?} end end p hash.delete_blank # => {"y"=>"content"} 
+15
source share

Here's a more general method:

 class Hash def deep_reject(&blk) self.dup.deep_reject!(&blk) end def deep_reject!(&blk) self.each do |k, v| v.deep_reject!(&blk) if v.is_a?(Hash) self.delete(k) if blk.call(k, v) end end end { a: 1, b: nil, c: { d: nil, e: '' } }.deep_reject! { |k, v| v.blank? } ==> { a: 1 } 
+4
source share

I think this is the most correct version:

 h = {a: {b: {c: "",}, d:1}, e:2, f: {g: {h:''}}} p = proc do |_, v| v.delete_if(&p) if v.respond_to? :delete_if v.nil? || v.respond_to?(:"empty?") && v.empty? end h.delete_if(&p) #=> {:a=>{:d=>1}, :e=>2} 
+3
source share

I know this thread is a bit outdated, but I came up with a better solution that supports multidimensional hashes. Does he use delete_if? except for its multidimensional one, it clears everything with an empty default value and, if a block is passed, it is passed through it by children.

 # Hash cleaner class Hash def clean! self.delete_if do |key, val| if block_given? yield(key,val) else # Prepeare the tests test1 = val.nil? test2 = val === 0 test3 = val === false test4 = val.empty? if val.respond_to?('empty?') test5 = val.strip.empty? if val.is_a?(String) && val.respond_to?('empty?') # Were any of the tests true test1 || test2 || test3 || test4 || test5 end end self.each do |key, val| if self[key].is_a?(Hash) && self[key].respond_to?('clean!') if block_given? self[key] = self[key].clean!(&Proc.new) else self[key] = self[key].clean! end end end return self end end 
+1
source share
 hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'} clean = proc{ |k,v| !v.empty? ? Hash === v ? v.delete_if(&clean) : false : true } hash.delete_if(&clean) #=> {"y"=>"content"} 

or like @sawa, you can use this process

 clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) } 
0
source share

Very little. If you want to remove the specified keys from the nested hash:

 def find_and_destroy(*keys) delete_if{ |k, v| (keys.include?(k.to_s) ? true : ( (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Array) ; (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Hash); false) )} end 

. You can also customize it further.

0
source share

All Articles