web-dev-qa-db-fra.com

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

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

116
Brian Jordan

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
61
opsb

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

134
jpemberthy

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
128
dgilperez

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
6
punund

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"}
5
dashvvv

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
5
Kelly Becker

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
4
mwalsher

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

3
srghma

Dans Simple one liner pour supprimer les valeurs NULL dans Hash,

rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? } 
3
ramya

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:

  • Remplacez v.blank? && v != false par v.nil? || v == "" pour supprimer strictement les chaînes vides et les valeurs nil
  • Remplacez v.blank? && v != false par v.nil? pour supprimer strictement les valeurs nil
  • Etc.

MODIFIÉ 2017/03/15 pour conserver les valeurs false et présenter d'autres options

3
Sebastian Jay

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
2
sahin

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.

2
Dmitry Polushkin

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.

0
user1519240

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
0
Varun Garg

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}
0
Rahul Patel

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
0
6ft Dan
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
0
Chix