Removing all empty elements from a hash / YAML?

前端 未结 20 1578
礼貌的吻别
礼貌的吻别 2020-12-07 15:35

How would I go about removing all empty elements (empty list items) from a nested Hash or YAML file?

相关标签:
20条回答
  • 2020-12-07 16:00

    Ruby's Hash#compact, Hash#compact! and Hash#delete_if! do not work on nested nil, empty? and/or blank? values. Note that the latter two methods are destructive, and that all nil, "", false, [] and {} values are counted as blank?.

    Hash#compact and Hash#compact! are only available in Rails, or Ruby version 2.4.0 and above.

    Here's a non-destructive solution that removes all empty arrays, hashes, strings and nil values, while keeping all false values:

    (blank? can be replaced with nil? or empty? as needed.)

    def remove_blank_values(hash)
      hash.each_with_object({}) do |(k, v), new_hash|
        unless v.blank? && v != false
          v.is_a?(Hash) ? new_hash[k] = remove_blank_values(v) : new_hash[k] = v
        end
      end
    end
    

    A destructive version:

    def remove_blank_values!(hash)
      hash.each do |k, v|
        if v.blank? && v != false
          hash.delete(k)
        elsif v.is_a?(Hash)
          hash[k] = remove_blank_values!(v)
        end
      end
    end
    

    Or, if you want to add both versions as instance methods on the Hash class:

    class Hash
      def remove_blank_values
        self.each_with_object({}) do |(k, v), new_hash|
          unless v.blank? && v != false
            v.is_a?(Hash) ? new_hash[k] = v.remove_blank_values : new_hash[k] = v
          end
        end
      end
    
      def remove_blank_values!
        self.each_pair do |k, v|
          if v.blank? && v != false
            self.delete(k)
          elsif v.is_a?(Hash)
            v.remove_blank_values!
          end
        end
      end
    end
    

    Other options:

    • Replace v.blank? && v != false with v.nil? || v == "" to strictly remove empty strings and nil values
    • Replace v.blank? && v != false with v.nil? to strictly remove nil values
    • Etc.

    EDITED 2017/03/15 to keep false values and present other options

    0 讨论(0)
  • 2020-12-07 16:02

    You can use Hash#reject to remove empty key/value pairs from a ruby Hash.

    # Remove empty strings
    { a: 'first', b: '', c: 'third' }.reject { |key,value| value.empty? } 
    #=> {:a=>"first", :c=>"third"}
    
    # Remove nil
    {a: 'first', b: nil, c: 'third'}.reject { |k,v| v.nil? } 
    # => {:a=>"first", :c=>"third"}
    
    # Remove nil & empty strings
    {a: '', b: nil, c: 'third'}.reject { |k,v| v.nil? || v.empty? } 
    # => {:c=>"third"}
    
    0 讨论(0)
  • 2020-12-07 16:03

    I believe it would be best to use a self recursive method. That way it goes as deep as is needed. This will delete the key value pair if the value is nil or an empty Hash.

    class Hash
      def compact
        delete_if {|k,v| v.is_a?(Hash) ? v.compact.empty? : v.nil? }
      end
    end
    

    Then using it will look like this:

    x = {:a=>{:b=>2, :c=>3}, :d=>nil, :e=>{:f=>nil}, :g=>{}}
    # => {:a=>{:b=>2, :c=>3}, :d=>nil, :e=>{:f=>nil}, :g=>{}} 
    x.compact
    # => {:a=>{:b=>2, :c=>3}}
    

    To keep empty hashes you can simplify this to.

    class Hash
      def compact
        delete_if {|k,v| v.compact if v.is_a?(Hash); v.nil? }
      end
    end
    
    0 讨论(0)
  • 2020-12-07 16:04

    Use hsh.delete_if. In your specific case, something like: hsh.delete_if { |k, v| v.empty? }

    0 讨论(0)
  • 2020-12-07 16:04

    compact_blank (Rails 6.1+)

    If you are using Rails (or a standalone ActiveSupport), starting from version 6.1, there is a compact_blank method which removes blank values from hashes.

    It uses Object#blank? under the hood for determining if an item is blank.

    { a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
    # => { b: 1, f: true }
    

    Here is a link to the docs and a link to the relative PR.

    A destructive variant is also available. See Hash#compact_blank!.


    If you need to remove only nil values,

    please, consider using Ruby build-in Hash#compact and Hash#compact! methods.

    { a: 1, b: false, c: nil }.compact
    # => { a: 1, b: false }
    
    0 讨论(0)
  • 2020-12-07 16:05

    You could add a compact method to Hash like this

    class Hash
      def compact
        delete_if { |k, v| v.nil? }
      end
    end
    

    or for a version that supports recursion

    class Hash
      def compact(opts={})
        inject({}) do |new_hash, (k,v)|
          if !v.nil?
            new_hash[k] = opts[:recurse] && v.class == Hash ? v.compact(opts) : v
          end
          new_hash
        end
      end
    end
    
    0 讨论(0)
提交回复
热议问题