How to recursively remove all keys with empty values from (YAML) hash?

前端 未结 6 1900
没有蜡笔的小新
没有蜡笔的小新 2021-01-04 22:21

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

This earlier post helped me to get it almost

相关标签:
6条回答
  • 2021-01-04 22:36

    I know this thread is a bit old but I came up with a better solution which supports Multidimensional hashes. It uses delete_if? except its multidimensional and cleans out anything with a an empty value by default and if a block is passed it is passed down through it's 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
    
    0 讨论(0)
  • 2021-01-04 22:41

    Here's a more generic 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 }
    
    0 讨论(0)
  • 2021-01-04 22:41

    Just a bit related thing. If you want to delete specified keys from 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 讨论(0)
  • 2021-01-04 22:45

    I think this 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}
    
    0 讨论(0)
  • 2021-01-04 22:49
    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 suggested, you can use this proc

    clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) }
    
    0 讨论(0)
  • 2021-01-04 22:55
    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 讨论(0)
提交回复
热议问题