การลบองค์ประกอบว่างทั้งหมดออกจากแฮช / YAML หรือไม่


คำตอบ:


70

คุณสามารถเพิ่มวิธีการที่กะทัดรัดให้กับ 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
ขนาดกะทัดรัดควรลบเฉพาะ nils ไม่ใช่ค่าเท็จ
Ismael Abreu

1
สิ่งนี้มีปัญหา: Hash#delete_ifเป็นการดำเนินการทำลายล้างในขณะที่compactวิธีการไม่ได้แก้ไขวัตถุ คุณสามารถใช้Hash#reject. หรือโทรมาที่เมธอดHash#compact!.
tokland

5
โปรดทราบว่าการcompactและcompact!มาตรฐานมาในรูบี => 2.4.0 และ Rails => 4.1 แม้ว่าจะไม่เกิดซ้ำ
aidan

เวอร์ชันเรียกซ้ำใช้ไม่ได้กับHashWithIndifferentAccess.. ตรวจสอบเวอร์ชันของฉันที่stackoverflow.com/a/53958201/1519240
user1519240

158

Rails 4.1 เพิ่มHash # compactและHash # 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
ดีและเป็นระเบียบเรียบร้อย แต่อาจเป็นที่น่าสังเกตว่าไม่เหมือนกับคำตอบที่ยอมรับส่วนขยาย Rails จะไม่เรียกซ้ำ?
SirRawlins

2
มันละเว้นแฮชที่ว่างเปล่า
Sebastian Palma

143

ใช้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); ศูนย์): v.empty? }; hsh.delete_if (& proc)
acw

3
@ แม้ดูเหมือนว่าพวกเขาจะได้ยินคุณ! api.rubyonrails.org/classes/Hash.html#method-i-compact (Rails 4.1)
dgilperez

2
สิ่งนี้จะทำให้NoMethodErrorif vเป็นศูนย์
Jerrod

6
คุณสามารถใช้. delete_if {| k, v | 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
รุ่นทางรถไฟที่ใช้งานได้กับค่าประเภทอื่นที่ไม่ใช่ Array, Hash หรือ String (เช่น Fixnum):swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
wdspkr

6

คุณสามารถใช้Hash # ปฏิเสธเพื่อลบคู่คีย์ / ค่าที่ว่างเปล่าออกจาก Ruby 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
FYI: แสดง.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

ปล. ตามคำตอบของใครบางคนหาไม่เจอ

การใช้งาน - 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

ฉันสร้างเมธอด 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#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ค่าอย่างเคร่งครัด
  • ฯลฯ

แก้ไข 2017/03/15 เพื่อรักษาfalseค่าและนำเสนอตัวเลือกอื่น ๆ


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

ใน Simple one liner สำหรับการลบค่า null ใน Hash

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

ระวังให้ดีblank?สำหรับสายที่ว่างเปล่าเช่นกัน
Hertzel Guinness

2

สามารถทำได้ด้วยไลบรารีfacets (คุณลักษณะที่ขาดหายไปจากไลบรารีมาตรฐาน) เช่น:

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

ทำงานร่วมกับ Enumerable (รวมถึง Array, Hash)

ดูวิธีการใช้งานแบบวนซ้ำ


1

https://stackoverflow.com/a/14773555/1519240เวอร์ชันเรียกซ้ำใช้งานได้ แต่ใช้ไม่ได้กับHashWithIndifferentAccessคลาสอื่น ๆ ที่เป็น Hash ..

นี่คือเวอร์ชันที่ฉันใช้:

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) จะรับคลาสอื่น ๆ ที่เหมือน Hash

คุณยังสามารถแทนที่inject({})ด้วยinject(HashWithIndifferentAccess.new)ถ้าคุณต้องการเข้าถึงแฮชใหม่โดยใช้ทั้งสัญลักษณ์และสตริง


1

compact_blank (ราง 6.1+)

หากคุณใช้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ค่า

โปรดพิจารณาใช้ Ruby build-in Hash#compactและHash#compact!method

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

0

ฉันเชื่อว่าควรใช้วิธีการเรียกซ้ำด้วยตนเอง วิธีนั้นจะลึกเท่าที่จำเป็น การดำเนินการนี้จะลบคู่ค่าคีย์หากค่าเป็นศูนย์หรือ Hash ว่างเปล่า

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

โปรดสังเกตว่า "เมื่อ Hash แล้วกระชับ (val) .empty?" ควรเป็น "เมื่อ Hash แล้ว val.compact.empty?"
AlexITC


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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.