it-swarm.com.de

Alle leeren Elemente aus einem Hash/YAML entfernen?

Wie entferne ich alle leeren Elemente (leere Listenelemente) aus einer verschachtelten Hash- oder YAML-Datei?

116
Brian Jordan

Sie könnten Hash so eine kompakte Methode hinzufügen

class Hash
  def compact
    delete_if { |k, v| v.nil? }
  end
end

oder für eine Version, die Rekursion unterstützt

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

Verwenden Sie hsh.delete_if . In Ihrem speziellen Fall etwa Folgendes: hsh.delete_if { |k, v| v.empty? }

134
jpemberthy

Rails 4.1 hinzugefügt Hash # compact und Hash # compact! als Kernerweiterung der Hash-Klasse von Ruby. Sie können sie wie folgt verwenden:

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                  
# => {}

Heads up: Diese Implementierung ist nicht rekursiv. Als Kuriosität wurde aus Performancegründen #select anstelle von #delete_if implementiert. Sehen Sie hier für die Benchmark .

Falls Sie es in Ihre Rails 3-App zurückportieren möchten:

# 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

Dieser würde auch leere Hash löschen:

swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash);  v.empty? }
hsh.delete_if &swoop
6
punund

Mit Hash # reject können Sie leere Schlüssel/Wert-Paare aus einem Ruby Hash entfernen.

# 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

Ich weiß, dass dieser Thread ein bisschen alt ist, aber ich habe eine bessere Lösung gefunden, die mehrdimensionale Hashes unterstützt. Es verwendet delete_if? mit Ausnahme des mehrdimensionalen Bereichs und löscht alle Elemente mit einem leeren Wert. Wenn ein Block übergeben wird, wird er durch seine untergeordneten Elemente weitergegeben.

# 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

Ich habe dafür eine deep_compact-Methode erstellt, die rekursiv keine Datensätze (und optional auch leere Datensätze) herausfiltert:

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

funktioniert sowohl für Hashes als auch für Arrays

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. basierend auf jemandes Antwort, kann nicht finden

3
srghma

In Simple one Liner zum Löschen von Nullwerten in Hash

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

Rubys Hash#compact, Hash#compact! und Hash#delete_if! funktionieren nicht mit verschachtelten nil-, empty?- und/oder blank?-Werten. Beachten Sie, dass die beiden letztgenannten Methoden destruktiv sind und dass alle nil, "", false, [] und {}-Werte als blank? gezählt werden.

Hash#compact und Hash#compact! sind nur in Rails oder Ruby Version 2.4.0 und höher verfügbar.

Hier ist eine zerstörungsfreie Lösung, die alle leeren Arrays, Hashes, Strings und nil-Werte entfernt und dabei alle false-Werte beibehält:

(blank? kann bei Bedarf durch nil? oder empty? ersetzt werden.)

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

Eine destruktive Version:

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

Oder, wenn Sie beide Versionen als Instanzmethoden für die Klasse Hash hinzufügen möchten:

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

Andere Optionen:

  • Ersetzen Sie v.blank? && v != false durch v.nil? || v == "", um leere Zeichenfolgen und nil-Werte strikt zu entfernen
  • Ersetzen Sie v.blank? && v != false durch v.nil?, um nil-Werte strikt zu entfernen
  • Usw.

BEARBEITET 2017/03/15, um false Werte beizubehalten und andere Optionen darzustellen

3
Sebastian Jay

in unserer Version: werden auch die leeren Zeichenfolgen und Nullwerte gelöscht

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

Könnte mit facets library (fehlenden Funktionen aus der Standardbibliothek) wie folgt gemacht werden:

require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }

Funktioniert mit jedem Enumerable (einschließlich Array, Hash).

Schauen Sie, wie rekursiv Methode implementiert wird.

2

Die rekursive Version von https://stackoverflow.com/a/14773555/1519240 funktioniert, jedoch nicht mit HashWithIndifferentAccess oder anderen Klassen, die eine Art Hash sind.

Hier ist die Version, die ich verwende:

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) akzeptiert mehr Klassen, die einem Hash ähneln.

Sie können inject({}) auch durch inject(HashWithIndifferentAccess.new) ersetzen, wenn Sie mit Symbol und String auf den neuen Hash zugreifen möchten.

0
user1519240

Folgendes habe ich:

# 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

Versuchen Sie dies, um nichts zu entfernen

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

Ich glaube, es wäre am besten, eine selbstrekursive Methode zu verwenden. Auf diese Weise geht es so tief wie nötig. Dadurch wird das Schlüsselwertpaar gelöscht, wenn der Wert null oder ein leerer Hash ist.

class Hash
  def compact
    delete_if {|k,v| v.is_a?(Hash) ? v.compact.empty? : v.nil? }
  end
end

Dann wird es so aussehen:

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}}

Um leere Hashes zu behalten, können Sie dies vereinfachen.

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