Bagaimana cara saya menghapus semua elemen kosong (daftar kosong item) dari file Hash atau YAML bersarang?
Bagaimana cara saya menghapus semua elemen kosong (daftar kosong item) dari file Hash atau YAML bersarang?
Jawaban:
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
Hash#delete_ifadalah operasi yang merusak, sementara compactmetode tidak mengubah objek. Anda bisa menggunakannya Hash#reject. Atau hubungi metode Hash#compact!.
compactdan compact!datang standar di Ruby => 2.4.0, dan Rails => 4.1. Mereka non-rekursif.
HashWithIndifferentAccess.. Periksa versi saya di stackoverflow.com/a/53958201/1519240
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
Gunakan hsh.delete_if . Dalam kasus spesifik Anda, sesuatu seperti:hsh.delete_if { |k, v| v.empty? }
proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)
NoMethodErrorif vis nil.
Jika Anda menggunakan Ruby 2.4+, Anda dapat menelepon compactdancompact!
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
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
swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
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"}
.empty?melempar kesalahan untuk angka-angka, sehingga Anda dapat menggunakan .blank?inRails
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)
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
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
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:
v.blank? && v != falsedengan v.nil? || v == ""untuk menghapus string dan nilnilai kosongv.blank? && v != falsedengan v.nil?untuk menghapus nilnilai-nilai secara ketatEDITED 2017/03/15 untuk menjaga falsenilai dan menyajikan opsi lain
Dalam Simple one liner untuk menghapus nilai null di Hash,
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
blank?berlaku untuk string kosong juga
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.
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
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
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}
hash.compact!
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.
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
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
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 }