Menghapus semua elemen kosong dari hash / YAML?

133

Bagaimana cara saya menghapus semua elemen kosong (daftar kosong item) dari file Hash atau YAML bersarang?

Brian Jordan
sumber

Jawaban:

70

Anda bisa menambahkan metode ringkas ke Hash seperti ini

class Hash
  def compact
    delete_if { |k, v| v.nil? }
  end
end

atau untuk versi yang mendukung rekursi

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
opsb
sumber
2
kompak seharusnya hanya menghilangkan nol. Bukan nilai
Ismael Abreu
1
Ini memiliki masalah: Hash#delete_ifadalah operasi yang merusak, sementara compactmetode tidak mengubah objek. Anda bisa menggunakannya Hash#reject. Atau hubungi metode Hash#compact!.
tokland
5
Harap dicatat bahwa compactdan compact!datang standar di Ruby => 2.4.0, dan Rails => 4.1. Mereka non-rekursif.
Aidan
Versi rekursif tidak berfungsi HashWithIndifferentAccess.. Periksa versi saya di stackoverflow.com/a/53958201/1519240
user1519240
157

Rails 4.1 menambahkan Hash # compact dan Hash # compact! sebagai ekstensi inti ke Hashkelas Ruby . Anda dapat menggunakannya seperti ini:

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

Kepala: implementasi ini tidak rekursif. Sebagai rasa ingin tahu, mereka mengimplementasikannya menggunakan #selectbukan #delete_ifkarena alasan kinerja. Lihat di sini untuk patokan .

Jika Anda ingin membuat backport ke aplikasi Rails 3 Anda:

# 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
dgilperez
sumber
3
Bagus dan rapi, tetapi mungkin perlu dicatat bahwa tidak seperti jawaban yang diterima ekstensi Rails tidak rekursif?
SirRawlins
2
Itu menghilangkan hash kosong.
Sebastian Palma
142

Gunakan hsh.delete_if . Dalam kasus spesifik Anda, sesuatu seperti:hsh.delete_if { |k, v| v.empty? }

jpemberthy
sumber
6
Rekursif:proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)
Daniel O'Hara
3
Saya percaya ada kesalahan ketik pada jawaban Anda yang benar: proc = Proc.new {| k, v | v.kind_of? (Hash)? (v.delete_if (& proc); nil): v.empty? }; hsh.delete_if (& proc)
acw
3
@Beven sepertinya mereka mendengarmu! api.rubyonrails.org/classes/Hash.html#method-i-compact (Rails 4.1)
dgilperez
2
Ini akan membuang NoMethodErrorif vis nil.
Jerrod
6
Anda dapat menggunakan .delete_if {| k, v | v.blank? }
Serhii Nadolynskyi
7

Yang ini juga akan menghapus hash yang kosong:

swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash);  v.empty? }
hsh.delete_if &swoop
punund
sumber
1
versi rails, yang juga berfungsi dengan nilai tipe selain Array, Hash, atau String (seperti Fixnum):swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
wdspkr
6

Anda dapat menggunakan Hash # reject untuk menghapus pasangan kunci / nilai kosong dari Hash 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"}
smd1000
sumber
4
FYI: .empty?melempar kesalahan untuk angka-angka, sehingga Anda dapat menggunakan .blank?inRails
illusionist
5

bekerja untuk hash dan array

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 berdasarkan jawaban someones, cant find

penggunaan - Helpers::RecursiveCompact.recursive_compact(something)

srghma
sumber
4

Saya tahu utas ini agak lama tetapi saya datang dengan solusi yang lebih baik yang mendukung hash Multidimensi. Ini menggunakan delete_if? kecuali multidimensi dan membersihkan apa pun dengan nilai kosong secara default dan jika blok dilewatkan maka akan diturunkan melalui anak-anak itu.

# 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
Kelly Becker
sumber
4

Saya membuat metode deep_compact untuk ini yang secara rekursif menyaring catatan nil (dan juga catatan kosong):

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
mwalsher
sumber
4

Ruby Hash#compact, Hash#compact!dan Hash#delete_if!tidak bekerja pada nested nil, empty?dan / atau blank?nilai - nilai. Perhatikan bahwa dua yang terakhir metode yang merusak, dan bahwa semua nil, "", false, []dan {}nilai-nilai dihitung sebagai blank?.

Hash#compactdan Hash#compact!hanya tersedia di Rails, atau Ruby versi 2.4.0 ke atas.

Berikut adalah solusi non-destruktif yang menghapus semua array kosong, hash, string, dan nilnilai, sembari menjaga semua falsenilai:

( blank?dapat diganti dengan nil?atau empty?sesuai kebutuhan.)

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

Versi destruktif:

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

Atau, jika Anda ingin menambahkan kedua versi sebagai metode instan di Hashkelas:

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

Pilihan lain:

  • Ganti v.blank? && v != falsedengan v.nil? || v == ""untuk menghapus string dan nilnilai kosong
  • Ganti v.blank? && v != falsedengan v.nil?untuk menghapus nilnilai-nilai secara ketat
  • Dll

EDITED 2017/03/15 untuk menjaga falsenilai dan menyajikan opsi lain

Sebastian Jay
sumber
3

versi kami: itu juga membersihkan string kosong dan nilai 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
sahin
sumber
3

Dalam Simple one liner untuk menghapus nilai null di Hash,

rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? } 
ramya
sumber
hati-hati, blank?berlaku untuk string kosong juga
Hertzel Guinness
2

Dapat dilakukan dengan facet library (fitur yang hilang dari perpustakaan standar), seperti itu:

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

Bekerja dengan Enumerable apa pun (termasuk Array, Hash).

Lihat bagaimana metode rekursif diimplementasikan.

Dmitry Polushkin
sumber
0

Saya percaya akan lebih baik menggunakan metode rekursif diri. Dengan begitu ia berjalan sedalam yang dibutuhkan. Ini akan menghapus pasangan nilai kunci jika nilainya nol atau Hash kosong.

class Hash
  def compact
    delete_if {|k,v| v.is_a?(Hash) ? v.compact.empty? : v.nil? }
  end
end

Kemudian menggunakannya akan terlihat seperti ini:

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

Untuk menyimpan hash kosong, Anda dapat menyederhanakan ini.

class Hash
  def compact
    delete_if {|k,v| v.compact if v.is_a?(Hash); v.nil? }
  end
end
6ft Dan
sumber
hmm referensi melingkar dapat mengarah ke loop IIUC tak terbatas.
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
Chix
sumber
Perhatikan bahwa "kapan Hash kemudian kompak (val). Kosong?" harus "kapan Hash lalu val.compact.empty?"
AlexITC
0

Coba ini untuk menghapus nol

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}
Rahul Patel
sumber
atau hanyahash.compact!
courtimas
0

Versi rekursif https://stackoverflow.com/a/14773555/1519240 berfungsi, tetapi tidak dengan HashWithIndifferentAccessatau kelas lain yang merupakan jenis Hash ..

Ini adalah versi yang saya gunakan:

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) akan menerima lebih banyak kelas yang seperti Hash.

Anda juga dapat mengganti inject({})dengan inject(HashWithIndifferentAccess.new)jika Anda ingin mengakses hash baru menggunakan simbol dan string.

pengguna1519240
sumber
0

Ini adalah sesuatu yang saya miliki:

# 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
Varun Garg
sumber
0

Nilai nih penghapusan dalam dari hash.

  # 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
Miro Mudrik
sumber
0

Jika Anda menggunakan Rails(atau mandiri ActiveSupport), mulai dari versi 6.1, ada compact_blankmetode yang menghapus blanknilai dari hash.

Ini digunakan di Object#blank?bawah tenda untuk menentukan apakah suatu barang kosong.

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

Berikut adalah tautan ke dokumen dan tautan ke PR relatif .

Varian destruktif juga tersedia. Lihat Hash#compact_blank!.


Jika Anda hanya perlu menghapus nilnilai,

tolong, pertimbangkan untuk menggunakan Ruby build-in Hash#compactdan Hash#compact!metode.

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