Удаление всех пустых элементов из хэша/YAML?

Как я могу удалить все пустые элементы (пустые элементы списка) из вложенного файла Hash или YAML?

Ответ 1

Вы можете добавить компактный метод для Hash, как этот

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

или для версии, поддерживающей рекурсию

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

Ответ 2

Используйте hsh.delete_if. В вашем конкретном случае что-то вроде: hsh.delete_if { |k, v| v.empty? }

Ответ 3

Rails 4.1 добавлен Hash # compact и Хэш # compact! как расширение ядра для класса Ruby Hash. Вы можете использовать их следующим образом:

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: эта реализация не является рекурсивной. Как любопытство, они использовали его, используя #select вместо #delete_if по соображениям производительности. См. здесь для эталона.

Если вы хотите выполнить резервное копирование его в приложение 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

Ответ 4

Этот тоже удалит пустые хеши:

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

Ответ 5

Я знаю, что эта ветка немного устарела, но я придумал лучшее решение, которое поддерживает многомерные хэши. Он использует 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

Ответ 6

Вы можете использовать Hash # reject для удаления пустых пар ключ/значение из рубинового хэша.

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

Ответ 7

Я сделал для этого метод deep_compact, который рекурсивно отфильтровывает записи nil (и, необязательно, пустые записи):

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

Ответ 8

наша версия: он также очищает пустые строки и значения 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

Ответ 9

В Простой один вкладыш для удаления нулевых значений в Hash,

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

Ответ 10

Может быть сделано с facets библиотека (отсутствующие функции из стандартной библиотеки), например:

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

Работает с любым Enumerable (включая Array, Hash).

Посмотрите, как реализуется рекурсивный метод.

Ответ 11

Ruby Hash#compact, Hash#compact! и Hash#delete_if! не работают с вложенными значениями nil, empty? и/или blank?. Обратите внимание, что последние два метода являются деструктивными и что все значения nil, "", false, [] и {} считаются blank?.

Hash#compact и Hash#compact! доступны только в Rails или Ruby версии 2.4.0 и выше.

Здесь неразрушающее решение, которое удаляет все пустые массивы, хэши, строки и значения nil, сохраняя все значения false:

(blank? может быть заменен на nil? или empty? по мере необходимости.)

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

Деструктивная версия:

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

Или, если вы хотите добавить обе версии в качестве методов экземпляра в классе 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

Другие параметры:

  • Замените v.blank? && v != false на v.nil? || v == "", чтобы строго удалить пустые строки и nil значения
  • Замените v.blank? && v != false на v.nil?, чтобы строго удалить значения nil
  • Etc.

EDITED 2017/03/15 сохранить значения false и представить другие параметры

Ответ 12

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

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

Затем его использование будет выглядеть так:

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

Чтобы сохранить пустые хеши, вы можете упростить это.

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

Ответ 13

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

Ответ 14

Попробуйте удалить 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}