Q:

Recursively removing `nil` and empty values from hash

I have a hash that starts out as:
{"request"=>{
  "security"=>{"username"=>{"type"=>nil}, "password"=>{"type"=>nil}},
  "order_i_d"=>{"type"=>nil, "description"=>nil},
  "order_number"=>{"type"=>nil},
  "show_kit_as_individual_s_k_us"=>false,
  "website_i_d"=>{"type"=>nil, "description"=>nil}
}}

And I'd like to recursively remove all values that are nil? and empty? but leave the falsey values in place. The final result should look like:
{"request"=>{
  "show_kit_as_individual_s_k_us"=>false
}}

How can I accomplish this?

A:

Just another implementation, written for fun & practice.

No monkey patching
Works on Hashes and Arrays
Can be used as a module function like: DeepCompact.deep_compact(hash)
Also a destructive target modifying variant: DeepCompact.deep_compact!(hash)
Can be used by extending on an existing object: { foo: nil }.extend(DeepCompact).deep_compact
Can be used via refinements: adding using DeepCompact to a file/class will bring deep_compact and deep_compact! to Hashes and Arrays for all code in that file/class.

Here's the module:
module DeepCompact
  [Hash, Array].each do |klass|
    refine klass do
      def deep_compact
        DeepCompact.deep_compact(self)
      end

      def deep_compact!
        DeepCompact.deep_compact!(self)
      end
    end
  end

  def self.extended(where)
    where.instance_exec do
      def deep_compact
        DeepCompact.deep_compact(self)
      end

      def deep_compact!
        DeepCompact.deep_compact!(self)
      end
    end
  end

  def deep_compact(obj)
    case obj
    when Hash
      obj.each_with_object({}) do |(key, val), obj|
        new_val = DeepCompact.deep_compact(val)
        next if new_val.nil? || (new_val.respond_to?(:empty?) && new_val.empty?)
        obj[key] = new_val
      end
    when Array
      obj.each_with_object([]) do |val, obj|
        new_val = DeepCompact.deep_compact(val)
        next if new_val.nil? || (new_val.respond_to?(:empty?) && new_val.empty?)
        obj << val
      end
    else
      obj
    end
  end
  module_function :deep_compact

  def deep_compact!(obj)
    case obj
    when Hash
      obj.delete_if do |_, val|
        val.nil? || (val.respond_to?(:empty?) && val.empty?) || DeepCompact.deep_compact!(val)
      end
      obj.empty?
    when Array
      obj.delete_if do |val|
        val.nil? || (val.respond_to?(:empty?) && val.empty?) || DeepCompact.deep_compact!(val)
      end
      obj.empty?
    else
      false
    end
  end
  module_function :deep_compact!
end

And then some examples on how to use it:
hsh = {
  'hello' => [
    'world',
    { 'and' => nil }
  ],
  'greetings' => nil,
  'salutations' => {
    'to' => { 'you' => true, 'him' => 'yes', 'her' => nil },
     'but_not_to' => nil
  }
}

puts "Original:"
pp hsh
puts
puts "Non-destructive module function:"
pp DeepCompact.deep_compact(hsh)
puts
hsh.extend(DeepCompact)
puts "Non-destructive after hash extended:"
pp hsh.deep_compact
puts
puts "Destructive refinement for array:"
array = [hsh]
using DeepCompact
array.deep_compact!
pp array

And the output:
Original:
{"hello"=>["world", {"and"=>nil}],
 "greetings"=>nil,
 "salutations"=>
  {"to"=>{"you"=>true, "him"=>"yes", "her"=>nil}, "but_not_to"=>nil}}

Non-destructive module function:
{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}

Non-destructive after hash extended:
{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}

Destructive refinement for array:
[{"hello"=>["world"], "salutations"=>{"to"=>{"you"=>true, "him"=>"yes"}}}]

Or just use one of the multiple gems that provide this for you.

