我该如何从嵌套的哈希或 YAML 文件中删除所有空元素(空列表项)?
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
# => {}
注意:此实现不是递归的。有趣的是,为了提高性能,他们使用了#select
而不是#delete_if
来实现它。请参见这里的基准测试。
如果你想将它移植到你的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
使用hsh.delete_if方法。在你的特定情况下,可以这样写:hsh.delete_if { |k, v| v.empty? }
proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)
可以翻译为:# 定义一个名为proc的Proc对象,删除哈希表中所有值为空或者为哈希表的键值对
proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&proc); nil) : v.empty? }
# 使用proc对象作为参数调用哈希表的delete_if方法
hsh.delete_if(&proc)
- Daniel O'Harav
是nil,这将抛出一个NoMethodError
。 - Jerrod你可以像这样为哈希表添加一个紧凑的方法
class Hash
def compact
delete_if { |k, v| v.nil? }
end
end
或者选择支持递归的版本。
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
Hash#delete_if
是一种破坏性操作,而compact
方法不会修改对象。你可以使用Hash#reject
。或者调用Hash#compact!
方法。 - toklandcompact
和 compact!
在 Ruby => 2.4.0 和 Rails => 4.1 中已经成为标准方法。但请注意它们不是递归的。 - aidanHashWithIndifferentAccess
不兼容。请查看我的版本:https://dev59.com/CXA75IYBdhLWcg3wJFgL#53958201 - user1519240compact
和transform_values
编写的递归实现! - Алексей Лещук若你使用的是Rails
(或者是独立的ActiveSupport
),从版本6.1
开始,可以使用compact_blank
方法来从散列中移除blank
值。
它在底层使用Object#blank?
来确定项是否为空。
{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }
也提供了一种破坏性变体。请参阅Hash#compact_blank!
。
如果您只需删除nil
值,请考虑使用Ruby内置的Hash#compact
和Hash#compact!
方法。
{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }
如果你正在使用 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
# 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"}
Rails
中,对于数字,.empty?
会抛出错误,因此您可以使用.blank?
。 - Shivaswoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.empty? }
hsh.delete_if &swoop
swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
- wdspkr适用于哈希表和数组
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
注:根据某人的回答,找不到
用法 - Helpers::RecursiveCompact.recursive_compact(某物)
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
我知道这篇文章有些旧了,但我提出了一种更好的解决方案,支持多维哈希。它使用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