web-dev-qa-db-ja.com

ハッシュ/ YAMLからすべての空の要素を削除しますか?

ネストされたHashまたはYAMLファイルからすべての空の要素(空のリストアイテム)を削除するにはどうすればよいですか?

127
Brian Jordan

このようにコンパクトなメソッドをハッシュに追加できます

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
68
opsb

Rails 4.1では、RubyのHashクラスのコア拡張として Hash#compact および Hash#compact! が追加されました。次のように使用できます。

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

ヘッズアップ:この実装は再帰的ではありません。好奇心として、パフォーマンス上の理由から、#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
140
dgilperez

hsh.delete_if を使用します。特定のケースでは、次のようなものです:hsh.delete_if { |k, v| v.empty? }

138
jpemberthy

これは空のハッシュも削除します:

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

Hash#reject を使用して、Rubyハッシュから空のキー/値ペアを削除できます。

# 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

私はこのスレッドが少し古いことを知っていますが、多次元ハッシュをサポートするより良いソリューションを思いつきました。 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
5
Kelly Becker

このために、nilレコード(およびオプションで空のレコードも)を再帰的に除外するdeep_compactメソッドを作成しました。

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

ハッシュと配列の両方で機能します

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

追伸誰かの答えに基づいて、見つけることができません

使用法-Helpers::RecursiveCompact.recursive_compact(something)

4
srghma

RubyのHash#compactHash#compact!およびHash#delete_if!は、ネストされたnilempty?および/またはblank?の値では機能しません。後者の2つのメソッドは破壊的であり、すべての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 != falsev.nil? || v == ""に置き換えて、空の文字列とnil値を厳密に削除します
  • v.blank? && v != falsev.nil?に置き換えて、nil値を厳密に削除します
  • 等。

false値を保持し、他のオプションを提示するように2017/03/15を編集

4
Sebastian Jay

ハッシュでnull値を削除するためのシンプルワンライナーでは、

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

バージョン:空の文字列と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
2
sahin

次のように facets library(標準ライブラリにない機能)で実行できます:

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

Enumerable(Array、Hashを含む)で動作します。

再帰的メソッド の実装方法を見てください。

2

https://stackoverflow.com/a/14773555/151924 の再帰バージョンは動作しますが、HashWithIndifferentAccessまたはハッシュのような他のクラスでは動作しません。

私が使用しているバージョンは次のとおりです。

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)は、ハッシュのようなクラスをさらに受け入れます。

シンボルと文字列の両方を使用して新しいハッシュにアクセスする場合は、inject({})inject(HashWithIndifferentAccess.new)に置き換えることもできます。

0
user1519240

ここに私が持っているものがあります:

# 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

これを試して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}
0
Rahul Patel

自己再帰的な方法を使用するのが最善だと思います。そうすれば、必要なだけ深くなります。これにより、値が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
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