简体   繁体   English

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

[英]Removing all empty elements from a hash / YAML?

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

Rails 4.1 added Hash#compact and Hash#compact! Rails 4.1 添加了Hash#compactHash#compact! as a core extensions to Ruby's Hash class.作为 Ruby Hash类的核心扩展。 You can use them like this:您可以像这样使用它们:

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                  
# => {}

Heads up: this implementation is not recursive.注意:这个实现不是递归的。 As a curiosity, they implemented it using #select instead of #delete_if for performance reasons.作为一种好奇心,他们实现了它使用#select ,而不是#delete_if出于性能的考虑。 See here for the benchmark .请参阅此处了解基准

In case you want to backport it to your Rails 3 app:如果您想将其向后移植到您的 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

Usehsh.delete_if .使用hsh.delete_if In your specific case, something like: hsh.delete_if { |k, v| v.empty? }在您的特定情况下,类似于: hsh.delete_if { |k, v| v.empty? } hsh.delete_if { |k, v| v.empty? }

You could add a compact method to Hash like this您可以像这样向 Hash 添加一个紧凑的方法

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

compact_blank (Rails 6.1+) 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.如果您使用Rails (或独立的ActiveSupport ),从版本6.1开始,有一个compact_blank方法可以从散列中删除blank值。

It uses Object#blank?它使用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 .这是文档链接和相关 PR链接

A destructive variant is also available.还可以使用破坏性变体。 See Hash#compact_blank!Hash#compact_blank! . .


If you need to remove only nil values,如果您只需要删除nil值,

please, consider using Ruby build-in Hash#compact and Hash#compact!请考虑使用 Ruby 内置的Hash#compactHash#compact! methods.方法。

{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }

If you're using Ruby 2.4+, you can call compact and compact!如果您使用的是 Ruby 2.4+,则可以调用compactcompact!

h = { a: 1, b: false, c: nil }
h.compact! #=> { a: 1, b: false }

https://ruby-doc.org/core-2.4.0/Hash.html#method-i-compact-21 https://ruby-doc.org/core-2.4.0/Hash.html#method-i-compact-21

This one would delete empty hashes too:这个也会删除空哈希:

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

You can use Hash#reject to remove empty key/value pairs from a ruby Hash.您可以使用Hash#reject从 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"}

works for both hashes and arrays适用于散列和数组

module Helpers
  module RecursiveCompact
    extend self

    def recursive_compact(hash_or_array)
      p = proc do |*args|
        v = args.last
        v.delete_if(&p) if v.respond_to? :delete_if
        v.nil? || v.respond_to?(:"empty?") && v.empty?
      end

      hash_or_array.delete_if(&p)
    end
  end
end

PS based on someones answer, cant find PS根据某人的回答,找不到

usage - Helpers::RecursiveCompact.recursive_compact(something)用法 - Helpers::RecursiveCompact.recursive_compact(something)

I know this thread is a bit old but I came up with a better solution which supports Multidimensional hashes.我知道这个线程有点旧,但我想出了一个支持多维哈希的更好的解决方案。 It uses delete_if?它使用 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

I made a deep_compact method for this that recursively filters out nil records (and optionally, blank records as well):我为此创建了一个 deep_compact 方法,它递归地过滤掉 nil 记录(以及可选的空白记录):

class Hash
  # Recursively filters out nil (or blank - e.g. "" 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's Hash#compact , Hash#compact! Ruby 的Hash#compactHash#compact! and Hash#delete_if!Hash#delete_if! do not work on nested nil , empty?不适用于嵌套的nilempty? and/or blank?和/或blank? values.值。 Note that the latter two methods are destructive, and that all nil , "" , false , [] and {} values are counted as blank?请注意,后两种方法是破坏性的,所有nil""false[]{}值都算作blank? . .

Hash#compact and Hash#compact! Hash#compactHash#compact! are only available in Rails, or Ruby version 2.4.0 and above.仅在 Rails 或 Ruby 2.4.0 及更高版本中可用。

Here's a non-destructive solution that removes all empty arrays, hashes, strings and nil values, while keeping all false values:这是一个非破坏性的解决方案,它删除所有空数组、散列、字符串和nil值,同时保留所有false值:

( blank? can be replaced with nil? or empty? as needed.) 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

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:或者,如果要将两个版本都添加为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

Other options:其他选项:

  • Replace v.blank? && v != false替换v.blank? && v != false v.blank? && v != false with v.nil? || v == "" v.blank? && v != falsev.nil? || v == "" v.nil? || v == "" v.nil? || v == "" to strictly remove empty strings and nil values v.nil? || v == ""严格删除空字符串和nil
  • Replace v.blank? && v != false替换v.blank? && v != false v.blank? && v != false with v.nil? v.blank? && v != falsev.nil? to strictly remove nil values严格删除nil
  • Etc.等等。

EDITED 2017/03/15 to keep false values and present other options编辑 2017/03/15 以保留false值并提供其他选项

our version: it also cleans the empty strings and nil values我们的版本:它还清除空字符串和 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

在用于删除哈希中的空值的简单单行中,

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

Could be done with facets library (a missing features from standard library), like that:可以使用facets库(标准库中缺少的功能)来完成,如下所示:

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

Works with any Enumerable (including Array, Hash).适用于任何可枚举(包括数组、哈希)。

Look how recursively method is implemented.看看递归方法是如何实现的。

The recursive version of https://stackoverflow.com/a/14773555/1519240 works, but not with HashWithIndifferentAccess or other classes that are kind of Hash.. https://stackoverflow.com/a/14773555/1519240的递归版本有效,但不适用于HashWithIndifferentAccess或其他类似 Hash 的类。

Here is the version I am using:这是我正在使用的版本:

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) will accept more classes that are like a Hash. kind_of?(Hash)将接受更多类似于 Hash 的类。

You can also replace inject({}) by inject(HashWithIndifferentAccess.new) if you want to access the new hash using both symbol and string.如果您想同时使用符号和字符串访问新的哈希,您也可以将inject({})替换为inject(HashWithIndifferentAccess.new)

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.如果值为 nil 或空哈希,这将删除键值对。

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

Try this to remove nil试试这个来删除 nil

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}

Here is something I have:这是我有的东西:

# 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

Deep deletion nil values from a hash.从散列中深度删除 nil 值。

  # 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

in ruby 2.7 there are standard compact and transform_values methods, on the Hash so you can do this like this:在 ruby​​ 2.7 中,Hash 上有标准的compacttransform_values方法,因此您可以这样做:

class Hash 
  def deep_compact
    compact.transform_values{|vl| vl.is_a?(Hash) ? vl.deep_compact : vl }
  end
end

It's the neatiest implementation, imho.这是最整洁的实现,恕我直言。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM