해시 / 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
compact는 nil 만 제거해야합니다. 잘못된 값이 아님
Ismael Abreu

1
이것은 문제가 있습니다 : Hash#delete_if파괴적인 작업이지만 compact메소드는 객체를 수정하지 않습니다. 사용할 수 있습니다 Hash#reject. 또는 메소드를 호출하십시오 Hash#compact!.
tokland

5
참고하시기 바랍니다 compactcompact!루비 => 2.4.0에서 기본으로 제공하고, 레일 => 4.1. 그래도 비재 귀적입니다.
aidan

재귀 버전은하지 않습니다와 작업 HashWithIndifferentAccess.. 나의 버전을 확인 stackoverflow.com/a/53958201/1519240
user1519240

157

Rails 4.1에는 Hash # compactHash # compact! 루비 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
멋지고 깔끔하지만 레일스 확장 프로그램이 재귀 적이 지 않다는 대답과 달리 주목할 가치가 있습니까?
SirRawlins

2
빈 해시를 생략합니다.
Sebastian Palma

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)
다니엘 오하라

3
나는 당신의 정답에 오타가 있다고 생각합니다. proc = Proc.new {| k, v | v.kind_of? (해시)? (v.delete_if (& proc); nil) : v.empty? }; hsh.delete_if (& proc)
acw

3
@BSeven은 그들이 당신을들은 것 같습니다! api.rubyonrails.org/classes/Hash.html#method-i-compact (Rails 4.1)
dgilperez

2
이것은 NoMethodErrorif vis nil을 던질 것 입니다.
Jerrod

6
.delete_if {| k, v |를 사용할 수 있습니다 블랭크? }
Serhii Nadolynskyi


7

이것은 빈 해시도 삭제합니다.

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

1
Rails 버전은 Array, Hash 또는 String 이외의 다른 유형의 값 (Fixnum과 같은)에서도 작동합니다.swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
wdspkr

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

4
참고 : .empty?숫자에 오류가 발생하므로 다음과 같이 사용할 수 있습니다 .blank?.Rails
illusionist

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

루비의 Hash#compact, Hash#compact!그리고 Hash#delete_if!중첩 된 작업을하지 않는 nil, empty?및 / 또는 blank?값. 후자의 두 가지 방법이 파괴적인 것으로, 모든 참고 nil, "", false, []{}값은 다음과 같이 계산된다 blank?.

Hash#compactHash#compact!레일에서만 사용할 수 있습니다, 또는 루비 버전 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?뿐만 아니라 빈 문자열에 간다
Hertzel 기네스

2

패싯 라이브러리 (표준 라이브러리에서 누락 된 기능)를 사용하여 다음과 같이 수행 할 수 있습니다 .

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

열거 형 (어레이, 해시 포함)과 함께 작동합니다.

재귀 적으로 방법 이 어떻게 구현 되는지 살펴보십시오 .


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 Guinness

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

"해시 할 때 compact (val) .empty?" "해시 때 val.compact.empty?"
AlexITC

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!
courtsimas

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

version 에서 시작 하여 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.