从哈希/ YAML中删除所有空元素?

我将如何去除嵌套散列或YAML文件中的所有空元素(空列表项)?

你可以添加一个紧凑的方法哈希像这样

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

或者支持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 

使用hsh.delete_if 。 在你的具体情况下,如: hsh.delete_if { |k, v| v.empty? } hsh.delete_if { |k, v| v.empty? }

Rails 4.1增加了Hash#compact和Hash#compact! 作为Ruby的Hash类的核心扩展。 你可以像这样使用它们:

 hash = { a: true, b: false, c: nil } hash.compact # => { a: true, b: false } hash # => { a: true, b: false, c: nil } hash.compact! # => { a: true, b: false } hash # => { a: true, b: false } { c: nil }.compact # => {} 

单挑:这个实现不是recursion的。 作为好奇心,他们使用#select而不是#select来实现它,出于性能原因。 看这里的基准 。

如果你想将它回溯到你的Rails 3应用程序:

 # config/initializers/rails4_backports.rb class Hash # as implemented in Rails 4 # File activesupport/lib/active_support/core_ext/hash/compact.rb, line 8 def compact self.select { |_, value| !value.nil? } end end 

这个也会删除空的哈希值:

 swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.empty? } hsh.delete_if &swoop 

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

我做了一个deep_compact方法,recursion地过滤掉nillogging(也可以是空白logging):

 class Hash # Recursively filters out nil (or blank - eg "" if exclude_blank: true is passed as an option) records from a Hash def deep_compact(options = {}) inject({}) do |new_hash, (k,v)| result = options[:exclude_blank] ? v.blank? : v.nil? if !result new_value = v.is_a?(Hash) ? v.deep_compact(options).presence : v new_hash[k] = new_value if new_value end new_hash end end end 

您可以使用哈希#拒绝从ruby哈希中删除空的键/值对。

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

我们的版本:它也清理空string和零值

 class Hash def compact delete_if{|k, v| (v.is_a?(Hash) and v.respond_to?('empty?') and v.compact.empty?) or (v.nil?) or (v.is_a?(String) and v.empty?) } end end 

在简单的一个线程删除Hash中的空值,

 rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? } 

可以用facet库(标准库中缺less的特征)来完成,就像这样:

 require 'hash/compact' require 'enumerable/recursively' hash.recursively { |v| v.compact! } 

适用于任何Enumerable(包括Array,Hash)。

看看recursion方法是如何实现的。

Ruby的Hash#compactHash#compact!Hash#delete_if! 不要在嵌套的nil上工作, empty? 和/或blank? 值。 请注意,后两种方法具有破坏性,并且所有nil""false[]{}值计为blank?

Hash#compactHash#compact! 仅在Rails或Ruby版本2.4.0及以上版本中可用。

这是一个非破坏性的解决scheme,它可以删除所有空数组,哈希,string和nil值,同时保留所有false值:

(根据需要, blank?可以用empty?empty?来replace。)

 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 

破坏版本:

 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 

或者,如果您想在Hash类中添加两个版本作为实例方法:

 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 

其他选项:

  • replacev.blank? && v != false v.blank? && v != falsev.nil? || v == "" v.nil? || v == "" v.nil? || v == ""严格删除空string和nil
  • replacev.blank? && v != false v.blank? && v != falsev.nil? 严格删除nil
  • 等等。

编辑2017/03/15保留false价值,并提出其他select

我相信最好使用自recursion方法。 这样它就可以根据需要进行深入。 这将删除键值对,如果值为零或空的散列。

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

然后使用它将看起来像这样:

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

为了保持空的散列,你可以简化它。

 class Hash def compact delete_if {|k,v| v.compact if v.is_a?(Hash); v.nil? } end end 
 class Hash def compact def _empty?(val) case val when Hash then val.compact.empty? when Array then val.all? { |v| _empty?(v) } when String then val.empty? when NilClass then true # ... custom checking end end delete_if { |_key, val| _empty?(val) } end end 

试试这个删除零

 hash = { a: true, b: false, c: nil } => {:a=>true, :b=>false, :c=>nil} hash.inject({}){|c, (k, v)| c[k] = v unless v.nil?; c} => {:a=>true, :b=>false}