Làm cách nào để xóa tất cả các thành phần trống (các mục danh sách trống) khỏi tệp Hash hoặc YAML lồng nhau?
Làm cách nào để xóa tất cả các thành phần trống (các mục danh sách trống) khỏi tệp Hash hoặc YAML lồng nhau?
Câu trả lời:
Bạn có thể thêm một phương thức nhỏ gọn vào Hash như thế này
class Hash
def compact
delete_if { |k, v| v.nil? }
end
end
hoặc cho một phiên bản hỗ trợ đệ quy
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_if
là một hoạt động phá hoại, trong khi compact
các phương thức không sửa đổi đối tượng. Bạn có thể sử dụng Hash#reject
. Hoặc gọi phương thức Hash#compact!
.
compact
và compact!
đạt tiêu chuẩn trong Ruby => 2.4.0 và Rails => 4.1. Họ không đệ quy mặc dù.
HashWithIndifferentAccess
.. Kiểm tra phiên bản của tôi tại stackoverflow.com/a/53958201/1519240
Rails 4.1 đã thêm Hash # compact và Hash # compact! như một phần mở rộng cốt lõi cho Hash
lớp của Ruby . Bạn có thể sử dụng chúng như thế này:
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
# => {}
Cảnh giác: việc thực hiện này không được đệ quy. Vì tò mò, họ đã triển khai nó bằng cách sử dụng #select
thay vì #delete_if
lý do hiệu suất. Xem ở đây cho điểm chuẩn .
Trong trường hợp bạn muốn backport nó vào ứng dụng Rails 3 của bạn:
# 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
Sử dụng hsh.delete_if . Trong trường hợp cụ thể của bạn, một cái gì đó như: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)
NoMethodError
nếu v
là con số không.
Nếu bạn đang sử dụng Ruby 2.4+, bạn có thể gọi compact
và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
Điều này cũng sẽ xóa băm trống:
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? }
Bạn có thể sử dụng từ chối Hash # để xóa các cặp khóa / giá trị trống khỏi 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?
ném lỗi cho các số, vì vậy bạn có thể sử dụng .blank?
trongRails
hoạt động cho cả băm và mảng
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 dựa trên câu trả lời của ai đó, không thể tìm thấy
sử dụng - Helpers::RecursiveCompact.recursive_compact(something)
Tôi biết chủ đề này là một chút cũ nhưng tôi đã đưa ra một giải pháp tốt hơn hỗ trợ băm đa chiều. Nó sử dụng xóa_if? ngoại trừ đa chiều và xóa sạch mọi thứ có giá trị trống theo mặc định và nếu một khối được truyền, nó sẽ được truyền qua các con của nó.
# 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
Tôi đã thực hiện một phương thức deep_compact cho phương pháp này để lọc đệ quy các bản ghi nil (và tùy chọn, cả các bản ghi trống):
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!
và Hash#delete_if!
không làm việc trên lồng nhau nil
, empty?
và / hoặc blank?
giá trị. Lưu ý rằng trong hai phương pháp thứ hai là phá hoại, và rằng tất cả nil
, ""
, false
, []
và {}
giá trị này được tính làblank?
.
Hash#compact
và Hash#compact!
chỉ khả dụng trong Rails, hoặc phiên bản Ruby 2.4.0 trở lên.
Đây là một giải pháp không phá hủy, loại bỏ tất cả các mảng, băm, chuỗi và nil
giá trị trống, trong khi vẫn giữ tất cả các false
giá trị:
( blank?
có thể được thay thế bằng nil?
hoặc empty?
khi cần thiết.)
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
Một phiên bản phá hoại:
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
Hoặc, nếu bạn muốn thêm cả hai phiên bản làm phương thức ví dụ trên Hash
lớp:
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
Sự lựa chọn khác:
v.blank? && v != false
bằng v.nil? || v == ""
để loại bỏ nghiêm ngặt các chuỗi trống vànil
giá trịv.blank? && v != false
bằng v.nil?
để loại bỏ nghiêm ngặt nil
các giá trịEDITED 2017/03/15 để giữ false
các giá trị và trình bày các tùy chọn khác
Trong Simple one liner để xóa các giá trị null trong Hash,
rec_hash.each {|key,value| rec_hash.delete(key) if value.blank? }
blank?
đi cho các chuỗi trống là tốt
Có thể được thực hiện với thư viện facets (một tính năng còn thiếu từ thư viện chuẩn), như thế:
require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }
Hoạt động với mọi Số lượng (bao gồm Mảng, Hash).
Nhìn cách phương pháp đệ quy được thực hiện.
Tôi tin rằng tốt nhất là sử dụng phương pháp tự đệ quy. Đó là cách nó đi sâu như cần thiết. Điều này sẽ xóa cặp giá trị khóa nếu giá trị bằng 0 hoặc Hash trống.
class Hash
def compact
delete_if {|k,v| v.is_a?(Hash) ? v.compact.empty? : v.nil? }
end
end
Sau đó, sử dụng nó sẽ trông như thế này:
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}}
Để giữ băm trống, bạn có thể đơn giản hóa việc này.
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
Hãy thử điều này để loại bỏ con số không
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!
Phiên bản đệ quy của https://stackoverflow.com/a/14773555/1519240 hoạt động, nhưng không phải với HashWithIndifferentAccess
hoặc các lớp khác là loại Hash ..
Đây là phiên bản tôi đang sử dụng:
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)
sẽ chấp nhận nhiều lớp giống như Hash.
Bạn cũng có thể thay thế inject({})
bằng inject(HashWithIndifferentAccess.new)
nếu bạn muốn truy cập hàm băm mới bằng cả ký hiệu và chuỗi.
Đây là thứ tôi có:
# 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
Xóa giá trị nil sâu từ một hàm băm.
# 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
Nếu bạn đang sử dụng Rails
(hoặc độc lập ActiveSupport
), bắt đầu từ phiên bản 6.1
, có một compact_blank
phương pháp loại bỏ blank
các giá trị khỏi giá trị băm.
Nó sử dụng Object#blank?
dưới mui xe để xác định nếu một mục trống.
{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }
Đây là một liên kết đến các tài liệu và một liên kết đến PR tương đối .
Một biến thể phá hoại cũng có sẵn. Xem Hash#compact_blank!
.
Nếu bạn chỉ cần xóa nil
các giá trị,
xin vui lòng, xem xét sử dụng tích hợp Hash#compact
và Hash#compact!
phương pháp Ruby .
{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }