2011-05-12 12 views
8

que han estado tratando de deshacerse de todas las claves hash en mi archivo YAML que tienen valores vacíos (en blanco) o hashes vacías como valores.¿Cómo eliminar recursivamente todas las claves con valores vacíos de hash (YAML)?

Este earlier post me ayudó a hacerlo bien, pero el recursivo de una línea sale de mi volcado de YAML con hashes vacíos cada vez que hay una anidación suficientemente profunda.

Realmente agradecería cualquier ayuda en esto. ¡Gracias!

proc = Proc.new { |k, v| (v.kind_of?(Hash) && !v.empty?) ? (v.delete_if(&proc); nil) : v.blank? } 

hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'} 
hash.delete_if(&proc) 

salida real

{"x"=>{"m"=>{}}, "y"=>"content"} 

deseado de salida

{"y"=>"content"} 

Respuesta

15
class Hash 
    def delete_blank 
    delete_if{|k, v| v.empty? or v.instance_of?(Hash) && v.delete_blank.empty?} 
    end 
end 

p hash.delete_blank 
# => {"y"=>"content"} 
+0

Mi versión (rieles): clase Hash delete_blank def delete_if { | k, v | v.blank? o (v.instance_of? (Hash) && v.delete_blank.empty?)} final final –

+1

blanco usuario? en lugar de estar vacío? porque fallará en nil – msroot

+0

¿Alguna razón por la que utilizas delete_if en lugar de rechazar? Usted – Donato

0
hash = {"x"=>{"m"=>{"n"=>{}}}, 'y' => 'content'} 
clean = proc{ |k,v| !v.empty? ? Hash === v ? v.delete_if(&clean) : false : true } 
hash.delete_if(&clean) 
#=> {"y"=>"content"} 

o como @sawa sugiere, se puede utilizar este proc

clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) } 
+1

deletreaste proc mal y su código no analiza –

+1

@AbePetrillo, nigromante;) – fl00r

0

Sólo un poco lo relacionado. Si desea borrar las claves específicas del resumen anidada:

def find_and_destroy(*keys) 
    delete_if{ |k, v| (keys.include?(k.to_s) ? true : ((v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Array) ; (v.each { |vv| vv = vv.find_and_destroy(*keys) }) if v.instance_of?(Hash); false))} 
end 

.También puede personalizar aún más

1

Sé que este hilo es un poco viejo, pero se me ocurrió una mejor solución que soporta los hashes multidimensionales. Utiliza delete_if? excepto que es multidimensional y borra todo lo que tiene un valor vacío por defecto y si se pasa un bloque se pasa a través de sus hijos.

# 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 
+0

Tenga cuidado al publicar copiar y pegar repetitivo/respuestas literales a múltiples preguntas, éstas tienden a ser marcado como "spam" por la comunidad. Si está haciendo esto, generalmente significa que las preguntas son duplicadas, así que márquelas como tales en su lugar: http://stackoverflow.com/a/12360142/419 – Kev

4

Aquí es un método más genérico:

class Hash 
    def deep_reject(&blk) 
    self.dup.deep_reject!(&blk) 
    end 

    def deep_reject!(&blk) 
    self.each do |k, v| 
     v.deep_reject!(&blk) if v.is_a?(Hash) 
     self.delete(k) if blk.call(k, v) 
    end 
    end 
end 

{ a: 1, b: nil, c: { d: nil, e: '' } }.deep_reject! { |k, v| v.blank? } 
==> { a: 1 } 
3

Creo que esta la versión más correcta:

h = {a: {b: {c: "",}, d:1}, e:2, f: {g: {h:''}}} 
p = proc do |_, v| 
    v.delete_if(&p) if v.respond_to? :delete_if 
    v.nil? || v.respond_to?(:"empty?") && v.empty? 
end 
h.delete_if(&p) 
#=> {:a=>{:d=>1}, :e=>2} 
+0

¿Puede explicar cómo funciona esto? –

+0

@DavidWest 'p' es un' proc' recursivo; si el valor 'v' es un objeto similar a un mapa (responde a' delete_if'), entonces podamos todo el valor vacío en él; si 'v' está vacío (o un valor' nil') entonces lo eliminamos (devuelve 'true' a' delete_if'). ¿Es suficiente esta explicación? :) – Iazel

+0

Muy elegante. Estoy impresionado. No estoy familiarizado con el ampersand. ¿Por qué se usa eso? Voy a ver los procs más después de leer esto. ¿Hay alguna palabra clave que pueda usar para aprender más sobre el signo? ¿Es como la que uno usa con & block? –

Cuestiones relacionadas