Ruby-on-rails 从哈希/YAML 中删除所有空元素?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3450641/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 23:00:54  来源:igfitidea点击:

Removing all empty elements from a hash / YAML?

ruby-on-railsrubyhashyaml

提问by Brian Jordan

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

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

采纳答案by opsb

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

回答by dgilperez

Rails 4.1 added Hash#compactand Hash#compact!as a core extensions to Ruby's Hashclass. You can use them like this:

Rails 4.1 添加了Hash#compactHash#compact!作为 RubyHash类的核心扩展。您可以像这样使用它们:

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 #selectinstead of #delete_iffor performance reasons. See here for the benchmark.

注意:这个实现不是递归的。出于好奇,他们使用#select而不是#delete_if出于性能原因实现了它。请参阅此处了解基准

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

回答by jpemberthy

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

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

回答by punund

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

回答by Wilson Silva

If you're using Ruby 2.4+, you can call compactand 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

回答by smd1000

You can use Hash#rejectto 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"}

回答by srghma

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

P.S. based on someones answer, cant find

PS根据某人的回答,找不到

usage - Helpers::RecursiveCompact.recursive_compact(something)

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

回答by Sebastian Jay

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

Ruby 的Hash#compact,Hash#compact!Hash#delete_if!不适用于嵌套的nil,empty?和/或blank?值。请注意,后两种方法是破坏性的,并且所有nil""false[]{}的值都算作blank?

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

Hash#compact并且Hash#compact!仅在 Rails 或 Ruby 2.4.0 及更高版本中可用。

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

这是一个非破坏性解决方案,它删除所有空数组、散列、字符串和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 Hashclass:

或者,如果您想将两个版本都添加为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 != falsewith v.nil? || v == ""to strictly remove empty strings and nilvalues
  • Replace v.blank? && v != falsewith v.nil?to strictly remove nilvalues
  • Etc.
  • 更换v.blank? && v != falsev.nil? || v == ""严格删除空字符串和nil
  • 更换v.blank? && v != falsev.nil?严格除去nil
  • 等等。

EDITED 2017/03/15 to keep falsevalues and present other options

编辑 2017/03/15 以保持false价值并提供其他选项

回答by Kelly Becker

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.

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

回答by mwalsher

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