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


回答:


70

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

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
コンパクトは釘を取り除くだけです。偽りのない値
Ismael Abreu

1
これには問題があります。これはHash#delete_if破壊的な操作ですが、compactメソッドはオブジェクトを変更しません。使用できますHash#reject。またはメソッドを呼び出しますHash#compact!
tokland

5
ことをしてくださいは、ノートcompactcompact!ルビー=> 2.4.0、およびRails => 4.1に標準装備されています。ただし、再帰的ではありません。
エイダン

再帰バージョンは動作しませんHashWithIndifferentAccess...で私のバージョンを確認しstackoverflow.com/a/53958201/1519240
user1519240

157

Rails 4.1はHash#compactHash#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                  
# => {}

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

3
すてきで整頓されていますが、認められた回答とは異なり、Rails拡張機能は再帰的ではないことに注意してください。
SirRawlins、2016

2
空のハッシュは省略されます。
セバスチャンパルマ

142

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


6
再帰的なもの:proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)
Daniel O'Hara

3
私はあなたのそうでなければ正しい答えにタイプミスがあると信じています:proc = Proc.new {| k、v | v.kind_of?(ハッシュ)?(v.delete_if(&proc); nil):v.empty?}; hsh.delete_if(&proc)
acw 2011

3
@BSeven彼らはあなたを聞いたようです!api.rubyonrails.org/classes/Hash.html#method-i-compact(Rails 4.1)
dgilperez 14

2
これはNoMethodErrorif vis nil をスローします。
Jerrod、2015年

6
.delete_if {| k、v |を使用できます v。空白?}
Serhii Nadolynskyi 2016


7

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

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

1
配列、ハッシュ、または文字列以外のタイプの値(Fixnumなど)でも機能するRailsバージョン:swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
wdspkr

6

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

4
FYI:.empty?あなたが使用できるように、数字のエラーをスロー.blank?Rails
イリュージョニスト

5

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

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誰かの回答に基づいて、見つけることができません

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


4

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

4

このために、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

RubyのHash#compactHash#compact!およびHash#delete_if!ネストされたや値nilでは機能しません。後者の2つの方法が破壊的であること、およびすべてのことに注意してください、、、およびの値は以下のようにカウントされ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 != falsev.nil? || v == ""、空の文字列を厳密に削除し、nil値ます
  • 交換するv.blank? && v != falsev.nil?厳密に削除するためにnil値を
  • 等。

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


3

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

3

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

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

blank?空の文字列にも注意してください
ヘルツェルギネス2015


0

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

うーん。循環参照は無限ループIIUCにつながる可能性があります。
Hertzelギネス2015

0
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

「Hashはその後、compact(val).empty?」「いつHashがval.compact.emptyなのか?」
AlexITC 2017年

0

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

または単にhash.compact!
18

0

https://stackoverflow.com/a/14773555/1519240の再帰バージョンは機能します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

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

# 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

ハッシュからのnil値の深い削除。

  # 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

0

バージョンRails(またはスタンドアロンActiveSupport)を使用している場合6.1は、ハッシュから値compact_blankを削除するメソッドがありblankます。

Object#blank?アイテムが空白かどうかを判別するために内部で使用されます。

{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }

ここにドキュメントへのリンクと相対PRへのリンクがあります。

破壊的なバリアントも利用できます。を参照してくださいHash#compact_blank!


nil値のみを削除する必要がある場合は、

RubyのビルトインHash#compactHash#compact!メソッドの使用を検討してください。

{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.