Как заменить все значения nil на "" в рубиновом хеше рекурсивно?

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

Как заменить все nil на хеш на "" так, чтобы хэш стал:

{"a"=>{"b"=>{"c"=>""}}}

Ответ 1

Вот рекурсивный метод, который не меняет исходный хэш.

код

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

<сильные > Примеры

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"=>"" } } 

Ответ 2

это уничтожит исходный хеш и не будет работать с хэшами с бесконечной рекурсией.

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

пример использования:

hash
 => {"a"=>{"b"=>{"c"=>nil}}} 
nil2empty(hash)
 => true 
hash
 => {"a"=>{"b"=>{"c"=>""}}} 

Ответ 3

Я знаю, что это не тот ответ, который вы ожидаете, но если вы можете обрабатывать значение вместо "", этот код работает

eval({"a"=>{"b"=>{"c"=>nil}}}.to_s.gsub("nil", "1")) #=> returns a hash #{"a"=>{"b"=>{"c"=>1}}}