Как рекурсивно удалить все ключи с пустыми значениями из (YAML) хеша? - PullRequest
9 голосов
/ 12 мая 2011

Я пытался избавиться от всех хеш-ключей в моем YAML-файле, которые имеют пустые (пустые) значения или пустые хеши в качестве значений.

Этот более ранний пост помог мне понять это почти правильно, но рекурсивная однострочная строка оставляет мой дамп YAML с пустыми хэшами всякий раз, когда происходит достаточно глубокое вложение.

Я был бы очень признателен за любую помощь в этом. Спасибо!

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)

Фактическая выработка

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

Желаемый вывод

{"y"=>"content"}

Ответы [ 6 ]

15 голосов
/ 12 мая 2011
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"}
5 голосов
/ 04 июня 2013

Вот более общий метод:

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 голосов
/ 13 июня 2014

Мне кажется, это самая правильная версия:

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}
1 голос
/ 11 сентября 2012

Я знаю, что этот поток немного староват, но я нашел лучшее решение, которое поддерживает многомерные хеши.Он использует delete_if?кроме его многомерного и очищает все с пустым значением по умолчанию, и если блок передается, он передается через его потомков.

# 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 голосов
/ 24 июля 2012

Просто немного связанная вещь.Если вы хотите удалить указанные ключи из вложенного хэша:

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

. Вы также можете настроить его дополнительно

0 голосов
/ 12 мая 2011
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"} 

или как предложил @sawa, вы можете использовать этот процесс

clean = proc{ |k,v| v.empty? or Hash === v && v.delete_if(&clean) }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...