From: Robert Klemme Date: 2010-03-01T17:12:29+09:00 Subject: Re: Iterate over hash of nested hashes 2010/2/28 Glenn Ritz : > Robert Klemme wrote: >> 2010/2/24 Xavier Noria : >>>>> >>>> {'en' => {'A' => Fixnum, 'B' => {'C' => Fixnum, 'D' => String }} >>> � �newh[k] = v.is_a?(Hash) ? classify_values(v) : v.class >>> �end >>> �newh >>> end >>> >>> p classify_values({'en' => {'A' => 1, 'B' => {'C' => 3, 'D' => 'four' }}}) >> >> I would do it a tad differently: >> >> def classify(o) >>   case o >>   when Hash >>     h = {} >>     o.each {|k,v| h[k] = classify(v)} >>     h >>   else >>     o.class >>   end >> end >> >> Advantage is that you can stuff anything in the method while your >> variant requires the argument to be a Hash.  The difference may seem >> subtle but if you add more collection types for special treatment, >> you'll will notice a difference in effort to implement it.  I can >> simply do >> >> def classify(o) >>   case o >>   when Hash >>     h = {} >>     o.each {|k,v| h[k] = classify(v)} >>     h >>   when Array >>     o.map {|v| classify(v)} >>   else >>     o.class >>   end >> end >> >> while you need to do a more complicated code change. >> >> Kind regards >> >> robert > > Thanks, this is very helpful. > > I am trying to adapt this a bit, but with no luck so far. > > What I'd like to do is to start with a hash and have the above code be > additive. > > In other words, if I have the following code: > > h0 = {} > h1 = {'en' => {'A' => 5, 'B' => { 'C' => 'xxx', 'D' => { 'E' => 4 }}}} > h2 = {'en' => {'F' => 'yyy'}} > > > I'd like to be able to call the classify method like this: > > puts h0.classify(h1).classify(h2).inspect > > And get the following result: > > {'en' => {'A' => Fixnum, 'B' => { 'C' => String, 'D' => { 'E' => Fixnum > }}, 'F' => String }} > > So I figured it would be something like this: > > class Hash >  def classify(o) >    case o >    when Hash >      h = self >      o.each {|k,v| h[k] = classify(v)} >      h >    when Array >      o.map {|v| classify(v)} >    else >      o.class >    end >  end > end > > But this is probably wrong in a few ways.  Any suggestions would be > appreciated. I would separate this in two steps: 1. merge Hashes intelligently (i.e. with a bit more logic than Hash#merge default behavior). 2. classify. So this would probably be something like class Hash def merge_deep!(hs) merge! hs do |key,old_val,new_val| case old_val when Hash old_val.merge_deep! new_val else new_val end end end end see http://www.ruby-doc.org/ruby-1.9/classes/Hash.html#M000406 Kind regards robert -- remember.guy do |as, often| as.you_can - without end http://blog.rubybestpractices.com/