How would I go about removing all empty elements (empty list items) from a nested Hash or YAML file?
How would I go about removing all empty elements (empty list items) from a nested Hash or YAML file?
If you are using Rails (or a standalone ActiveSupport), starting from version 6.1, there is a compact_blank method which removes blank values from hashes.
It uses Object#blank? under the hood for determining if an item is blank.
{ a: "", b: 1, c: nil, d: [], e: false, f: true }.compact_blank
# => { b: 1, f: true }
Here is a link to the docs and a link to the relative PR.
A destructive variant is also available. See Hash#compact_blank!.
If you need to remove only nil values,
please, consider using Ruby build-in Hash#compact and Hash#compact! methods.
{ a: 1, b: false, c: nil }.compact
# => { a: 1, b: false }
If you're using Ruby 2.4+, you can call compact and 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
works for both hashes and 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
P.S. based on someones answer, cant find
usage - Helpers::RecursiveCompact.recursive_compact(something)
our version: it also cleans the empty strings and nil values
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
Could be done with facets library (a missing features from standard library), like that:
require 'hash/compact'
require 'enumerable/recursively'
hash.recursively { |v| v.compact! }
Works with any Enumerable (including Array, Hash).
Look how recursively method is implemented.
The recursive version of https://stackoverflow.com/a/14773555/1519240 works, but not with HashWithIndifferentAccess or other classes that are kind of Hash..
Here is the version I am using:
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) will accept more classes that are like a Hash.
You can also replace inject({}) by inject(HashWithIndifferentAccess.new) if you want to access the new hash using both symbol and string.
Here is something I have:
# 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
Deep deletion nil values from a 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
in ruby 2.7 there are standard compact and transform_values methods, on the Hash so you can do this like this:
class Hash
def deep_compact
compact.transform_values{|vl| vl.is_a?(Hash) ? vl.deep_compact : vl }
end
end
It's the neatiest implementation, imho.