Vedanshu Vedanshu - 6 months ago 18
Ruby Question

How to trasform all values in a nested hash?

I want to convert all the values in a nested hash to a utf8 compatible string. I initially thought this would be easy and something like

should be available for me to use, but I am unable to find anything this simple on a quick google and SO search.

I do not want to write (maintain) a method similar to the lines of Change values in a nested hash . Is there a native API implementation or a shorthand available for this or do I have to write my own method?


Interesting to learn of the deep_merge approach taken in the answer by "The F". Here is another approach which requires adding a few helper methods.

First, the helper methods:

From the top answer here (converting-a-nested-hash-into-a-flat-hash):

def flat_hash(h,f=[],g={})
  return g.update({ f=>h }) unless h.is_a? Hash
  h.each { |k,r| flat_hash(r,f+[k],g) }

From a Github repo called ruby-bury (this functionality was proposed to Ruby core, but rejected)

class Hash
  def bury *args
    if args.count < 2
      raise"2 or more arguments required")
    elsif args.count == 2
      self[args[0]] = args[1]
      arg = args.shift
      self[arg] = {} unless self[arg]
      self[arg].bury(*args) unless args.empty?

And then a method tying it together:

def change_all_values(hash, &blk)    
  # the next line makes the method "pure functional"
  # but can be removed otherwise.
  hash = Marshal.load(Marshal.dump(hash))

  flat_hash(hash).each { |k,v| hash.bury(*(k + [])) }

A usage example:

irb(main):063:0> a = {a: 1, b: { c: 1 } }
=> {:a=>1, :b=>{:c=>1}}
irb(main):064:0> b = change_all_values(a) { |val| val + 1 }
=> {:a=>2, :b=>{:c=>2}}
irb(main):066:0> a
=> {:a=>1, :b=>{:c=>1}}