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

时间:2011-05-12 08:44:56

标签: ruby-on-rails ruby hash 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"}

6 个答案:

答案 0 :(得分: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"}

答案 1 :(得分:5)

这是一个更通用的方法:

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 }

答案 2 :(得分: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}

答案 3 :(得分: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

答案 4 :(得分: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) }

答案 5 :(得分: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

。您还可以进一步自定义