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?
Just another implementation, written for fun & practice.
DeepCompact.deep_compact(hash)
DeepCompact.deep_compact!(hash)
{ foo: nil }.extend(DeepCompact).deep_compact
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.
Came up with the following:
class Hash
def deep_compact!
self.each_pair do |key, value|
if value.is_a?(Hash)
value.deep_compact!
end
if value.nil? || (value.is_a?(Hash) && value.empty?)
self.delete(key)
end
end
end
end
Another option:
class Hash
def deep_transform(&block)
self.inject({}){|result, (key,value)|
value = if Hash === value
value.deep_transform(&block)
else
value
end
block.call(result,key,value)
result
}
end
def deep_compact
self.deep_transform do |result, key, value|
if value.nil? || (value.is_a?(Hash) && value.empty?)
# Don't Keep
else
result[key] = value
end
end
end
end
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.