Wie würde ich vorgehen, um alle leeren Elemente (leere Listenelemente) aus einer verschachtelten Hash- oder YAML-Datei zu entfernen?
Wie würde ich vorgehen, um alle leeren Elemente (leere Listenelemente) aus einer verschachtelten Hash- oder YAML-Datei zu entfernen?
Antworten:
Sie können Hash wie folgt 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
Hash#delete_if
handelt sich um eine destruktive Operation, während compact
Methoden das Objekt nicht ändern. Sie können verwenden Hash#reject
. Oder rufen Sie die Methode auf Hash#compact!
.
compact
und compact!
kommen standardmäßig in Ruby => 2.4.0 und Rails => 4.1. Sie sind jedoch nicht rekursiv.
HashWithIndifferentAccess
. Überprüfen Sie meine Version unter stackoverflow.com/a/53958201/1519240
Rails 4.1 hat Hash # compact und Hash # compact hinzugefügt ! als Kernerweiterung von Rubys Hash
Klasse. Sie können sie folgendermaßen 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. Aus Neugier haben sie es #select
anstelle von #delete_if
aus Leistungsgründen implementiert . Siehe hier für den Benchmark .
Falls Sie es auf 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
Verwenden Sie hsh.delete_if . In Ihrem speziellen Fall so etwas wie: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
Wenn v
ist Null.
Wenn Sie Ruby 2.4+ verwenden, können Sie compact
und anrufencompact!
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
Dieser würde auch leere Hashes löschen:
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? }
Mit Hash # ablehnen 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"}
.empty?
Ihrer Information: Wirft Fehler für Zahlen, so dass Sie .blank?
inRails
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
PS basierend auf jemandes Antwort, kann nicht finden
Verwendung - Helpers::RecursiveCompact.recursive_compact(something)
Ich weiß, dass dieser Thread etwas alt ist, aber ich habe eine bessere Lösung gefunden, die mehrdimensionale Hashes unterstützt. Es verwendet delete_if? außer es ist mehrdimensional und bereinigt standardmäßig alles mit einem leeren Wert. Wenn ein Block übergeben wird, wird er über 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
Ich habe dafür eine deep_compact-Methode erstellt, die rekursiv keine Datensätze herausfiltert (und optional auch leere Datensätze):
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!
und Hash#delete_if!
nicht die Arbeit an verschachtelten nil
, empty?
und / oder blank?
Werte. Man beachte , dass die beiden letztgenannten Methoden destruktiv sind, und dass alle nil
, ""
, false
, []
und {}
Werte werden gezählt alsblank?
.
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 durch nil?
oder empty?
nach Bedarf 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 zur Hash
Klasse 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:
v.blank? && v != false
durch v.nil? || v == ""
, um leere Zeichenfolgen und strikt zu entfernennil
Wertev.blank? && v != false
durch v.nil?
, um nil
Werte strikt zu entfernenEDITED 2017/03/15, um false
Werte zu halten und andere Optionen zu präsentieren
In Simple One Liner zum Löschen von Nullwerten in Hash,
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
blank?
geht auch für leere Saiten
Könnte mit Facettenbibliothek (eine fehlende Funktion aus der Standardbibliothek) wie folgt gemacht werden:
require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }
Funktioniert mit allen Enumerable (einschließlich Array, Hash).
Schauen Sie, wie die rekursive Methode implementiert ist.
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 sieht es folgendermaßen aus:
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
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
Versuchen Sie dies, um Null 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}
hash.compact!
Die rekursive Version von https://stackoverflow.com/a/14773555/1519240 funktioniert, jedoch nicht mitHashWithIndifferentAccess
oder anderen Klassen, die eine Art Hash sind.
Hier ist die Version, die ich benutze:
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 wie ein Hash sind.
Sie können auch ersetzen inject({})
durch , inject(HashWithIndifferentAccess.new)
wenn Sie den neuen Hash unter Verwendung von sowohl Symbol und String zugreifen möchten.
Hier ist etwas, was ich habe:
# 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
Tiefes Löschen von Nullwerten aus einem Hash.
# 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
Wenn Sie ab Version Rails
(oder Standalone ActiveSupport
) verwenden, 6.1
gibt es eine compact_blank
Methode, mit der blank
Werte aus Hashes entfernt werden.
Es wird Object#blank?
unter der Haube verwendet, um festzustellen, ob ein Artikel leer ist.
{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }
Hier ist ein Link zu den Dokumenten und ein Link zur relativen PR .
Eine destruktive Variante ist ebenfalls erhältlich. Siehe Hash#compact_blank!
.
Wenn Sie nur nil
Werte entfernen müssen ,
Bitte erwägen Sie die Verwendung von Ruby Build-In Hash#compact
und Hash#compact!
Methoden.
{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }