15
str = "<a><b><c></c></b></a>"
hash = Hash.from_xml(str)
# => {"a"=>{"b"=>{"c"=>nil}}}

How can I replace all nils in a Hash to "" so that the hash becomes:

{"a"=>{"b"=>{"c"=>""}}}
sawa
  • 165,429
  • 45
  • 277
  • 381
ohho
  • 50,879
  • 75
  • 256
  • 383

3 Answers3

14

Here is a recursive method that does not change the original hash.

Code

def denilize(h)
  h.each_with_object({}) { |(k,v),g|
    g[k] = (Hash === v) ?  denilize(v) : v.nil? ? '' : v }
end

Examples

h = { "a"=>{ "b"=>{ "c"=>nil } } }
denilize(h) #=> { "a"=>{ "b"=>{ "c"=>"" } } }

h = { "a"=>{ "b"=>{ "c"=>nil , "d"=>3, "e"=>nil}, "f"=>nil  } }
denilize(h) #=> { "a"=>{ "b"=>{ "c"=>"" , "d"=>3, "e"=>""}, "f"=>"" } } 
Cary Swoveland
  • 106,649
  • 6
  • 63
  • 100
2

this will destroy the original hash and will not work with hashes with infinite recursion.

def nil2empty(hash)
  hash.keys.each do |key|
    if hash[key].kind_of? Hash
      nil2empty(hash[key])
    else
      hash[key] = '' if hash[key].nil?
    end
  end
  true # of course, what else? :P
end

example of usage:

hash
 => {"a"=>{"b"=>{"c"=>nil}}} 
nil2empty(hash)
 => true 
hash
 => {"a"=>{"b"=>{"c"=>""}}} 
fotanus
  • 19,618
  • 13
  • 77
  • 111
  • Since your method destroys the original hash, the appropriate method name would be `nil2empty!` via Ruby convention. Rather, if we were to go even more into convention, the method name would be `nil_to_empty!` and it would return `nil`. – David May 28 '14 at 05:41
  • @David the bang methods convention destroy the caller object. nil2empty is not a method on the Hash model and it don't change the called (which I don't even know who is), I think is for the best leave without the bang. Also, accordingly to [this style guide](https://github.com/styleguide/ruby) (which is the one I usually use) the bangs method should only exist if there is a non-bang method as well. – fotanus May 28 '14 at 05:43
  • 1
    Ah, yes, you're right. Would probably be appropriate to monkey-patch something like this into `Hash` though... wherein if one did that my conventions would apply :P. – David May 28 '14 at 05:44
  • To avoid destroying the original hash you could of course first make a deep copy. One way is with [Marshal#dump](http://www.ruby-doc.org/core-2.0/Marshal.html#method-c-dump) and [Marshal#load](http://www.ruby-doc.org/core-2.0/Marshal.html#method-c-load): `hash_copy = Marshal.load(Marshal.dump(hash))`. – Cary Swoveland May 28 '14 at 06:04
0

I know this is not the answer you are expecting, but if you could handle a value instead of "" , this code works

eval({"a"=>{"b"=>{"c"=>nil}}}.to_s.gsub("nil", "1")) #=> returns a hash #{"a"=>{"b"=>{"c"=>1}}}
sameera207
  • 16,547
  • 19
  • 87
  • 152