Comment procéder pour supprimer tous les éléments vides (éléments de liste vides) d'un fichier Hash ou YAML imbriqué?
Comment procéder pour supprimer tous les éléments vides (éléments de liste vides) d'un fichier Hash ou YAML imbriqué?
Réponses:
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 prenant 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
Hash#delete_if
c'est une opération destructive, alors que les compact
méthodes ne modifient pas l'objet. Vous pouvez utiliser Hash#reject
. Ou appelez la méthode Hash#compact!
.
compact
et compact!
venir en standard dans Ruby => 2.4.0, et Rails => 4.1. Ils ne sont cependant pas récursifs.
HashWithIndifferentAccess
.. Vérifiez ma version sur stackoverflow.com/a/53958201/1519240
Rails 4.1 a ajouté Hash # compact et Hash # compact! comme extensions de base de la Hash
classe 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
plutôt que #delete_if
pour des raisons de performances. Voir ici pour le benchmark .
Si vous souhaitez le rétroporter sur 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
Utilisez hsh.delete_if . Dans votre cas spécifique, quelque chose comme: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)
NoMethodError
si v
est nul.
Si vous utilisez Ruby 2.4+, vous pouvez appeler compact
etcompact!
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
Celui-ci supprimerait également les hachages vides:
swoop = 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? }
Vous pouvez utiliser Hash # Rejeter pour supprimer des paires clé / valeur vides d'un Hash 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"}
.empty?
jette une erreur pour les nombres, vous pouvez donc utiliser .blank?
dansRails
fonctionne à la fois pour les hachages 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
PS basé sur la réponse de quelqu'un, impossible de trouver
utilisation - Helpers::RecursiveCompact.recursive_compact(something)
Je sais que ce fil est un peu vieux mais j'ai trouvé une meilleure solution qui prend en charge les hachages multidimensionnels. Il utilise delete_if? sauf qu'il est multidimensionnel et nettoie tout ce qui a une valeur vide par défaut et si un bloc est passé, il est passé à travers 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 pour cela qui filtre récursivement les enregistrements nuls (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
Ruby Hash#compact
, Hash#compact!
et Hash#delete_if!
ne fonctionnent pas sur imbriquée nil
, empty?
et / ou des blank?
valeurs. Notez que les deux dernières méthodes sont destructrices, et que tous nil
, ""
, false
, []
et les {}
valeurs sont considérées commeblank?
.
Hash#compact
et Hash#compact!
ne sont disponibles que dans Rails ou Ruby version 2.4.0 et supérieure.
Voici une solution non destructive qui supprime tous les tableaux vides, les hachages, les chaînes et les nil
valeurs, tout en conservant toutes les false
valeurs:
( blank?
peut être remplacé par nil?
ou empty?
au besoin.)
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 destructrice:
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 Hash
classe:
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 etnil
valeursv.blank? && v != false
par v.nil?
pour supprimer strictement les nil
valeursEDITED 2017/03/15 pour conserver les false
valeurs et présenter d'autres options
Dans Simple one liner pour supprimer les valeurs nulles dans Hash,
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
blank?
va aussi pour les cordes vides
Peut être fait avec la bibliothèque de facettes (une fonctionnalité manquante de la bibliothèque standard), comme ça:
require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }
Fonctionne avec n'importe quel Enumerable (y compris Array, Hash).
Regardez comment la méthode est implémentée de manière récursive .
Je pense qu'il serait préférable d'utiliser une méthode auto-récursive. De cette façon, il va aussi loin que nécessaire. Cela supprimera la paire clé / valeur si la valeur est nulle 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 conserver les hachages vides, vous pouvez simplifier cela en.
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
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}
hash.compact!
La version récursive de https://stackoverflow.com/a/14773555/1519240 fonctionne, mais pas avec HashWithIndifferentAccess
ou avec d' autres classes qui sont en quelque sorte Hash.
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 en utilisant à la fois le symbole et 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
Suppression profonde des valeurs nulles d'un hachage.
# 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
Si vous utilisez Rails
(ou une version autonome ActiveSupport
), à partir de la version 6.1
, il existe une compact_blank
méthode qui supprime les blank
valeurs des hachages.
Il utilise Object#blank?
sous le capot pour déterminer si un élément est vide.
{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }
Voici un lien vers les documents et un lien vers le PR relatif .
Une variante destructive est également disponible. Voir Hash#compact_blank!
.
Si vous devez supprimer uniquement des nil
valeurs,
s'il vous plaît, pensez à utiliser l'intégration Hash#compact
et les Hash#compact!
méthodes Ruby .
{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }