[英]Removing all empty elements from a hash / YAML?
我将如何从嵌套的 Hash 或 YAML 文件中删除所有空元素(空列表项)?
Rails 4.1 added Hash#compact and Hash#compact! Rails 4.1 添加了Hash#compact和Hash#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
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#compact
和Hash#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+,则可以调用compact
和compact!
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#compact
, Hash#compact!
and Hash#delete_if!
和Hash#delete_if!
do not work on nested nil
, empty?
不适用于嵌套的nil
, empty?
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#compact
和Hash#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:其他选项:
v.blank? && v != false
替换v.blank? && v != false
v.blank? && v != false
with v.nil? || v == ""
v.blank? && v != false
与v.nil? || v == ""
v.nil? || v == ""
v.nil? || v == ""
to strictly remove empty strings and nil
values v.nil? || v == ""
严格删除空字符串和nil
值v.blank? && v != false
替换v.blank? && v != false
v.blank? && v != false
with v.nil?
v.blank? && v != false
与v.nil?
to strictly remove nil
values严格删除nil
值 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 上有标准的compact
和transform_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.