एक हैश / 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
कॉम्पैक्ट को केवल निल्स को हटा देना चाहिए। मिथ्या भाव नहीं
इस्माईल अब्रू

1
यह एक समस्या है: Hash#delete_ifएक विनाशकारी ऑपरेशन है, जबकि compactविधियां ऑब्जेक्ट को संशोधित नहीं करती हैं। आप उपयोग कर सकते हैं Hash#reject। या विधि को बुलाओ Hash#compact!
20

5
कृपया ध्यान दें कि compactऔर compact!रूबी => 2.4.0, और रेल => 4.1 में मानक आएं। वे हालांकि गैर-पुनरावर्ती हैं।
हेल्पन

पुनरावर्ती संस्करण के साथ काम नहीं करता है HashWithIndifferentAccess.. मेरे संस्करण को stackoverflow.com/a/53958201/1519240 पर देखें
user1519240

157

रेल्स 4.1 ने हैश # कॉम्पैक्ट और हैश # कॉम्पैक्ट जोड़ा ! रूबी की 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बेंचमार्क के लिए यहां देखें ।

यदि आप इसे अपने रेल 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
अच्छा और सुव्यवस्थित, लेकिन शायद यह ध्यान देने योग्य है कि स्वीकृत उत्तर के विपरीत रेल एक्सटेंशन पुनरावर्ती नहीं है?
सिरावलिन

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)
डैनियल ओ'हारा

3
मेरा मानना ​​है कि आपके अन्यथा सही उत्तर में एक टाइपो है: proc = Proc.new {| k, v | v.kind_of (? हैश)? (v.delete_if (& proc); nil): v.empty? }; hsh.delete_if (& proc)
8

3
@ यह लगता है कि वे तुम्हें सुना है! api.rubyonrails.org/classes/Hash.html#method-i-compact (रेल 4.1)
dgilperez

2
यह एक NoMethodErrorअगर vnil फेंक देगा ।
जेरोड

6
आप उपयोग कर सकते हैं .delete_if {| k, v | v.blank? }
सेरही नाडोलिंस्की

8

यदि आप रूबी 2.4+ का उपयोग कर रहे हैं, तो आप कॉल कर सकते हैं compactऔरcompact!

h = { a: 1, b: false, c: nil }
h.compact! #=> { a: 1, b: false }

https://ruby-doc.org/core-2.4.0/Hash.html#method-i-compact-21


1
मुझे यह भी उपयोगी लगता है कि Hash.except (: कुंजी) विधि है
फायरड्रैगन

7

यह खाली हैश को भी हटा देगा:

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

1
रेल संस्करण, जो कि ऐरे, हैश या स्ट्रिंग (जैसे swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
फिक्सनम

6

आप रूबी हैश से खाली कुंजी / मान जोड़े निकालने के लिए Hash # अस्वीकार का उपयोग कर सकते हैं ।

# 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
जादूगर

5

hashes और arrays दोनों के लिए काम करता है

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

मैंने इसके लिए एक गहरी_कंपैक्ट विधि बनाई जो पुन: शून्य रिकॉर्ड (और वैकल्पिक रूप से, रिक्त रिकॉर्ड के साथ ही) को फ़िल्टर करती है:

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#compactऔर Hash#compact!केवल रेल, या रूबी संस्करण २.४.० और इसके बाद के संस्करण में उपलब्ध हैं।

यहां एक गैर-विनाशकारी समाधान है जो 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मान
  • आदि।

falseमूल्यों को बनाए रखने और अन्य विकल्प प्रस्तुत करने के लिए ED/03 2017/03/15


3

हमारा संस्करण: यह खाली तारों और शून्य मानों को भी साफ करता है

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

हैश में अशक्त मूल्यों को हटाने के लिए सरल एक लाइनर में,

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

सावधान, blank?खाली तारों के लिए भी जाता है
हर्ट्ज़ेल गिनीज

2

पहलुओं पुस्तकालय (मानक पुस्तकालय से एक लापता सुविधाओं) के साथ किया जा सकता है , जैसे:

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

किसी भी Enumerable (ऐरे, हैश सहित) के साथ काम करता है।

देखो कैसे पुनरावर्ती विधि को लागू किया जाता है।


0

मेरा मानना ​​है कि आत्म पुनरावर्ती पद्धति का उपयोग करना सबसे अच्छा होगा। इस तरह यह उतना ही गहरा होता है जितना की जरूरत होती है। यदि मान शून्य या खाली हैश है, तो यह कुंजी मान युग्म को हटा देगा।

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 को जन्म दे सकता है।
हर्ट्जेल गिनीज

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

ध्यान दें कि "जब हैश तब कॉम्पैक्ट (वैल) है। खाली?" होना चाहिए "जब हैश तो val.compact.empty?"
एलेक्सिटक

0

नील को हटाने के लिए यह प्रयास करें

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!
अदालतों

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

हैश से डीप डिलीशन निल मान।

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

यहाँ डॉक्स का लिंक और रिश्तेदार पीआर का लिंक दिया गया है

एक विनाशकारी संस्करण भी उपलब्ध है। देख लो Hash#compact_blank!


यदि आपको केवल nilमान निकालने की आवश्यकता है ,

कृपया, रूबी बिल्ड-इन Hash#compactऔर Hash#compact!विधियों का उपयोग करने पर विचार करें ।

{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.