159 votes

Suppression de tous les éléments vides d'un hachage / YAML ?

Comment faire pour supprimer tous les éléments vides (éléments de liste vides) d'un fichier Hash ou YAML imbriqué ?

180voto

dgilperez Points 1021

Ajout de Rails 4.1 Hash#compact y Hash#compact ! en tant qu'extension de base de l'extension de Ruby Hash classe. 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ée en utilisant #select au lieu de #delete_if pour des raisons de performance. Voir ici pour le benchmark .

Au cas où vous souhaiteriez l'intégrer à 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

3 votes

C'est bien fait, mais cela vaut la peine de noter que, contrairement à la réponse acceptée, l'extension Rails n'est pas récursive ?

2 votes

Il omet les hachages vides.

147voto

jpemberthy Points 4063

Utilisez hsh.delete_if . Dans votre cas spécifique, quelque chose comme : hsh.delete_if { |k, v| v.empty? }

6 votes

Récursif : proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)

3 votes

Je crois qu'il y a une faute de frappe dans votre réponse, par ailleurs correcte : proc = Proc.new { |k, v| v.kind_of ?(Hash) ? (v.delete_if(&proc) ; nil) : v.empty ? } ; hsh.delete_if(&proc)

0 votes

Ce serait bien si #compact était ajouté à Hash.

73voto

opsb Points 6860

Vous pourriez 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 supporte la récursion

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

2 votes

Compact ne devrait supprimer que les nils. Valeurs non fantaisistes

2 votes

Il y a un problème : Hash#delete_if est une opération destructive, tandis que compact Les méthodes ne modifient pas l'objet. Vous pouvez utiliser Hash#reject . Ou appelez la méthode Hash#compact! .

5 votes

Veuillez noter que compact y compact! sont standard dans Ruby => 2.4.0, et Rails => 4.1. Ils sont cependant non récursifs.

7voto

punund Points 997

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

1 votes

Version rails, qui fonctionne également avec des valeurs d'autres types que Array, Hash, ou String (comme Fixnum) : swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }

4voto

Kelly Becker Points 58

Je sais que ce fil est un peu vieux mais j'ai trouvé une meilleure solution qui supporte 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 transmis vers le bas à 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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X