如何以递归方式从(YAML)哈希中删除具有空值的所有键?

我一直试图摆脱我的YAML文件中具有空(空白)值或空哈希值作为值的所有哈希键。

这个早期的post帮助我几乎把它弄好了,但是当有足够深的嵌套时,递归的单行会使我的YAML转储空闲哈希。

我真的很感激任何帮助。 谢谢!

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) 

实际输出

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

期望的输出

 {"y"=>"content"} 

 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"} 

这是一个更通用的方法:

 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 } 

我认为这是最正确的版本:

 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} 

我知道这个线程有点旧,但我想出了一个更好的解决方案,它支持多维哈希。 它使用delete_if? 除了它的多维并且默认情况下清除任何具有空值的东西,如果传递了一个块,它将通过它的子项向下传递。

 # 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 

只是有点相关的事情。 如果要从嵌套哈希中删除指定的键:

 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 

你也可以进一步定制它

 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"} 

或者像@sawa建议的那样,你可以使用这个proc

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