Removing all empty elements from a hash / YAML?
Solution 1
You could add a compact method to Hash like this
class Hash
def compact
delete_if { |k, v| v.nil? }
end
end
or for a version that supports recursion
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
Solution 2
Rails 4.1 added Hash#compact and Hash#compact! as a core extensions to Ruby's Hash
class. You can use them like this:
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
# => {}
Heads up: this implementation is not recursive. As a curiosity, they implemented it using #select
instead of #delete_if
for performance reasons. See here for the benchmark.
In case you want to backport it to your Rails 3 app:
# 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
Solution 3
Use hsh.delete_if. In your specific case, something like: hsh.delete_if { |k, v| v.empty? }
Solution 4
compact_blank (Rails 6.1+)
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 }
Solution 5
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
Related videos on Youtube
Brian Jordan
Game and web developer in San Francisco. Curator of Coding for Interviews, weekly CS topic overview and programming interview practice problem.
Updated on June 19, 2021Comments
-
Brian Jordan almost 3 years
How would I go about removing all empty elements (empty list items) from a nested Hash or YAML file?
-
Daniel O'Hara almost 14 yearsRecursive one:
proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&l); nil) : v.empty? }; hsh.delete_if(&proc)
-
acw over 12 yearsI believe there is a typo in your otherwise correct answer: proc = Proc.new { |k, v| v.kind_of?(Hash) ? (v.delete_if(&proc); nil) : v.empty? }; hsh.delete_if(&proc)
-
Ismael over 10 yearscompact should only remove nils. Not falsy values
-
B Seven over 10 yearsIt would be great if #compact was added to Hash.
-
wdspkr about 10 yearsrails version, that also works with values of other types than Array, Hash, or String (like Fixnum):
swoop = Proc.new { |k, v| v.delete_if(&swoop) if v.kind_of?(Hash); v.blank? }
-
dgilperez over 9 years@BSeven it seems they heard you! api.rubyonrails.org/classes/Hash.html#method-i-compact (Rails 4.1)
-
Hertzel Guinness almost 9 yearscareful,
blank?
goes for empty strings as well -
Hertzel Guinness almost 9 yearshmm. circular references could lead to infinite loop IIUC.
-
Jerrod over 8 yearsThis will throw a
NoMethodError
ifv
is nil. -
Serhii Nadolynskyi over 8 yearsYou can use .delete_if { |k, v| v.blank? }
-
SirRawlins over 7 yearsNice and tidy, but probably worth noting that unlike the accepted answer the Rails extension isn't recursive?
-
tokland over 7 yearsThis has a problem:
Hash#delete_if
is a destructive operation, whilecompact
methods don't modify the object. You can useHash#reject
. Or call the methodHash#compact!
. -
illusionist over 7 yearsFYI:
.empty?
throws error for numbers, so you can use.blank?
inRails
-
AlexITC over 7 yearsNote that "when Hash then compact(val).empty?" should be "when Hash then val.compact.empty?"
-
aidan over 6 yearsPlease note that
compact
andcompact!
come standard in Ruby => 2.4.0, and Rails => 4.1. They are non-recursive though. -
A moskal escaping from Russia about 6 yearsIt omits empty hashes.
-
user1519240 over 5 yearsThe recursive version does not work with
HashWithIndifferentAccess
.. Check my version at stackoverflow.com/a/53958201/1519240 -
courtsimas almost 5 yearsor simply
hash.compact!
-
FireDragon about 4 yearsI also find useful is the Hash.except(:key) method
-
Алексей Лещук almost 3 yearsCheck out mine recursive implementation based on standard
compact
andtransform_values
below!