Comment procéder pour supprimer tous les éléments vides (éléments de liste vides) d'un fichier Hash ou YAML imbriqué?
Vous pouvez ajouter une méthode compacte à Hash comme ceci
class Hash
def compact
delete_if { |k, v| v.nil? }
end
end
ou pour une version qui prend en charge la récursivité
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
Utilisez hsh.delete_if . Dans votre cas particulier, quelque chose comme: hsh.delete_if { |k, v| v.empty? }
Rails 4.1 ajouté Hash # compact et Hash # compact! en tant qu’extension essentielle de la classe Hash
de Ruby. Vous pouvez les utiliser comme ceci:
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
# => {}
Attention: cette implémentation n'est pas récursive. Par curiosité, ils l'ont implémenté en utilisant #select
au lieu de #delete_if
pour des raisons de performances. Voir ici pour la référence .
Au cas où vous voudriez le transférer vers votre application 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
Celui-ci supprimerait aussi les hachages vides:
swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.empty? }
hsh.delete_if &swoop
Vous pouvez utiliser Hash # refuse pour supprimer les paires clé/valeur vides d’un Ruby Hash.
# 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"}
Je sais que ce fil est un peu vieux mais je suis venu avec une meilleure solution qui prend en charge le hachage multidimensionnel. Il utilise delete_if? excepté son caractère multidimensionnel et nettoie tout ce qui a une valeur vide par défaut et si un bloc est passé, il est transmis par ses enfants.
# 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
J'ai créé une méthode deep_compact qui filtre de manière récursive les enregistrements nil (et éventuellement, les enregistrements vides également):
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
fonctionne pour les tables de hachage et les tableaux
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. basé sur la réponse de quelqu'un, impossible de trouver
Dans Simple one liner pour supprimer les valeurs NULL dans Hash,
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
Les Hash#compact
, Hash#compact!
et Hash#delete_if!
de Ruby ne fonctionnent pas avec les valeurs imbriquées nil
, empty?
et/ou blank?
. Notez que les deux dernières méthodes sont destructives et que toutes les valeurs nil
, ""
, false
, []
et {}
sont comptées comme blank?
.
Hash#compact
et Hash#compact!
sont uniquement disponibles dans Rails ou dans les versions 2.4.0 et supérieures de Ruby.
Voici une solution non destructive qui supprime tous les tableaux, hachages, chaînes et valeurs nil
vides, tout en conservant toutes les valeurs false
:
(blank?
peut être remplacé par nil?
ou empty?
selon les besoins.)
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
Une version destructive:
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
Ou, si vous souhaitez ajouter les deux versions en tant que méthodes d'instance sur la classe 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
Autres options:
v.blank? && v != false
par v.nil? || v == ""
pour supprimer strictement les chaînes vides et les valeurs nil
v.blank? && v != false
par v.nil?
pour supprimer strictement les valeurs nil
MODIFIÉ 2017/03/15 pour conserver les valeurs false
et présenter d'autres options
notre version: elle nettoie aussi les chaînes vides et les valeurs 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
Pourrait être fait avec facets library (une des fonctionnalités manquantes de la bibliothèque standard), comme ceci:
require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }
Fonctionne avec tout type Enumerable (y compris Array, Hash).
Regardez comment méthode récursive est implémentée.
La version récursive de https://stackoverflow.com/a/14773555/1519240 fonctionne, mais pas avec HashWithIndifferentAccess
ou d'autres classes qui sont un peu du hachage ..
Voici la version que j'utilise:
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)
acceptera plus de classes qui ressemblent à un hachage.
Vous pouvez également remplacer inject({})
par inject(HashWithIndifferentAccess.new)
si vous souhaitez accéder au nouveau hachage à l'aide du symbole et de la chaîne.
Voici quelque chose que j'ai:
# 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
Essayez ceci pour supprimer 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}
Je crois qu'il serait préférable d'utiliser une méthode auto-récursive. De cette façon, il va aussi profond que nécessaire. Cela supprimera la paire valeur/clé si la valeur est nil ou un hachage vide.
class Hash
def compact
delete_if {|k,v| v.is_a?(Hash) ? v.compact.empty? : v.nil? }
end
end
Ensuite, l'utiliser ressemblera à ceci:
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}}
Pour garder les hachages vides, vous pouvez simplifier cela.
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