2011-05-12 83 views
8

我一直试图摆脱我的YAML文件中有空(空)值或空散列作为值的所有散列键。如何递归地从(YAML)散列中删除所有具有空值的键?

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

回答

15
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"} 
+0

我的版本(导轨): 类哈希 高清delete_blank delete_if { | k,v | v.blank?或(v.instance_of?(Hash)&& v.delete_blank.empty?)} end end – 2011-09-13 12:20:20

+1

User blank?而不是空的?因为它会在零 – msroot 2014-07-02 00:23:09

+0

失败你为什么使用delete_if而不是拒绝? – Donato 2016-03-21 21:30:15

0
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) } 
+1

你拼写错误,你的代码不解析 – 2014-04-02 13:40:15

+1

@AbePetrillo,necromncer;) – fl00r 2014-04-02 18:39:34

0

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

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 

。您还可以自定义其进一步

1

我知道这个线程是有点老,但我想出了一个更好的解决方案,它支持多维哈希值。它使用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 
+0

在发布复制和粘贴样板/逐字回答多个问题时要小心,这些问题往往会被社区标记为“垃圾”。如果你这样做,那么它通常意味着问题是重复的,所以将它们标记为:http://stackoverflow.com/a/12360142/419 – Kev 2012-09-10 23:26:18

4

这里有一个更通用的方法:

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

我觉得这是最正确的版本:

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} 
+0

你能解释它是如何工作的吗? – 2015-01-23 21:13:42

+0

@DavidWest 'p'是一个递归的'proc'; 如果值'v'是一个类似地图的对象(响应'delete_if'),那么我们修剪其中的所有空值; 如果'v'为空(或'nil'值),那么我们删除它(返回'true'到'delete_if')。 这个解释够了吗? :) – Iazel 2015-01-25 11:33:58

+0

非常优雅。我很佩服。我不熟悉&符号。为什么使用?阅读完本书后,我将更多地关注procs。是否有关键字可用于了解有关&符号的更多信息?它是否像使用&block一样? – 2015-01-26 01:29:43

相关问题