ネストされたHashまたはYAMLファイルからすべての空の要素(空のリストアイテム)を削除するにはどうすればよいですか?
このようにコンパクトなメソッドをハッシュに追加できます
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
Rails 4.1では、RubyのHash
クラスのコア拡張として Hash#compact および Hash#compact! が追加されました。次のように使用できます。
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? }
これは空のハッシュも削除します:
swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.empty? }
hsh.delete_if &swoop
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"}
私はこのスレッドが少し古いことを知っていますが、多次元ハッシュをサポートするより良いソリューションを思いつきました。 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
このために、nilレコード(およびオプションで空のレコードも)を再帰的に除外するdeep_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
ハッシュと配列の両方で機能します
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(something)
RubyのHash#compact
、Hash#compact!
およびHash#delete_if!
は、ネストされたnil
、empty?
および/またはblank?
の値では機能しません。後者の2つのメソッドは破壊的であり、すべてのnil
、""
、false
、[]
および{}
値はblank?
としてカウントされることに注意してください。 。
Hash#compact
およびHash#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
値を厳密に削除しますfalse
値を保持し、他のオプションを提示するように2017/03/15を編集
ハッシュでnull値を削除するためのシンプルワンライナーでは、
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
バージョン:空の文字列と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
https://stackoverflow.com/a/14773555/151924 の再帰バージョンは動作しますが、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)
は、ハッシュのようなクラスをさらに受け入れます。
シンボルと文字列の両方を使用して新しいハッシュにアクセスする場合は、inject({})
をinject(HashWithIndifferentAccess.new)
に置き換えることもできます。
ここに私が持っているものがあります:
# 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
これを試して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}
自己再帰的な方法を使用するのが最善だと思います。そうすれば、必要なだけ深くなります。これにより、値が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
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