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

163

我该如何从嵌套的哈希或 YAML 文件中删除所有空元素(空列表项)?

22个回答

4
Ruby的Hash#compactHash#compact!Hash#delete_if!方法无法处理嵌套的nilempty?和/或blank?值。请注意,后两种方法是具有破坏性的,并且所有的nil""false[]{}值都被视为blank?Hash#compactHash#compact!仅在Rails或Ruby版本2.4.0及以上可用。
以下是一种非破坏性解决方案,可删除所有空数组、哈希、字符串和nil值,同时保留所有false值:
blank?可以根据需要替换为nil?empty?。)
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

其他选项:

  • v.blank? && v != false替换为v.nil? || v == ""以严格删除空字符串和nil
  • v.blank? && v != false替换为v.nil?以严格删除nil
  • 等等。

编辑于2017/03/15,保留false值并提供其他选项。


3

可以使用facets库来完成(这是标准库缺失的功能),像这样:

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

适用于任何可枚举对象(包括数组、哈希表)。

看一下递归方法的实现。


3
我们的版本: 它还清除空字符串和nil值。
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

3

在哈希表中删除空值的简单一行代码:

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

小心,blank? 也适用于空字符串。 - Hertzel Guinness

1

https://dev59.com/CXA75IYBdhLWcg3wJFgL#14773555的递归版本是可行的,但是不能与HashWithIndifferentAccess或其他类似哈希的类一起使用。

以下是我正在使用的版本:

def recursive_compact
  inject({}) do |new_hash, (k,v)|
    if !v.nil?
      new_hash[k] = v.kind_of?(Hash) ? v.recursive_compact : v
    end
    new_hash
  end
end

kind_of?(Hash)可以接受更多类似于Hash的类。

如果您想使用符号和字符串访问新哈希表,还可以将inject({})替换为inject(HashWithIndifferentAccess.new)


0

我认为最好使用自递归方法。这样它可以深入到需要的程度。如果值为nil或空哈希,则会删除键值对。

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

如果我理解正确,循环引用可能会导致无限循环。 - Hertzel Guinness

0
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

请注意,“when Hash then compact(val).empty?” 应该是 “when Hash then val.compact.empty?”。 - AlexITC

0
在 Ruby 2.7 中,哈希表有标准的 compacttransform_values 方法,因此您可以这样做:
class Hash 
  def deep_compact
    compact.transform_values{|vl| vl.is_a?(Hash) ? vl.deep_compact : vl }
  end
end

在我看来,这是最整洁的实现。


0

这是我拥有的东西:

# recursively remove empty keys (hashes), values (array), hashes and arrays from hash or array
def sanitize data
  case data
  when Array
    data.delete_if { |value| res = sanitize(value); res.blank? }
  when Hash
    data.delete_if { |_, value| res = sanitize(value); res.blank? }
  end
  data.blank? ? nil : data
end

0
从哈希表中深度删除空值。
  # returns new instance of hash with deleted nil values
  def self.deep_remove_nil_values(hash)
    hash.each_with_object({}) do |(k, v), new_hash|
      new_hash[k] = deep_remove_nil_values(v) if v.is_a?(Hash)
      new_hash[k] = v unless v.nil?
    end
  end

  # rewrite current hash
  def self.deep_remove_nil_values!(hash)
    hash.each do |k, v|
      deep_remove_nil_values(v) if v.is_a?(Hash)
      hash.delete(k) if v.nil?
    end
  end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接