From: Mike Gold Date: 2009-01-02T17:34:09+09:00 Subject: Re: Help using ruby enumerator idioms Mischa Fierer wrote: > Hi, > > In Ruby, when I see something like: > > h = [] > > x.each do > > ... > > end > > h > > Usually means that I can refactor it to use map, inject, select, or > reject. Yes, I do the same. In a broader sense, functional style is often shorter and less error-prone. Map, inject, select are just examples of the functional idiom smuggled into ruby. In this case, a hash-of-hash merge function is needed to reorder keys in a clean way. # # hoh_merge( # { :x => { :y => :foo } }, # { :x => { :z => :bar } } # ) # #=> {:x=>{:y=>:foo, :z=>:bar}} # def hoh_merge(a, b) b.inject(a) { |acc, (key, inner_hash)| acc.merge( key => ( if existing = a[key] existing.merge(inner_hash) else inner_hash end ) ) } end data = { :flintstone => { :husband => :fred, :wife => :wilma, }, :rubble => { :husband => :barney, :wife => :betty, }, } # original imperative version h = Hash.new { |hash, key| hash[key] = Hash.new } data.each { |hash_key, inner_hash| inner_hash.each { |key, value| h[key][hash_key] = value } } # pure functional version h2 = data.inject(Hash.new) { |acc, (hash_key, inner_hash)| inner_hash.keys.inject(acc) { |inner_acc, key| hoh_merge(inner_acc, key => { hash_key => inner_hash[key] }) } } require 'pp' pp h pp h2 #=> # {:wife=>{:rubble=>:betty, :flintstone=>:wilma}, # :husband=>{:rubble=>:barney, :flintstone=>:fred}} # {:wife=>{:rubble=>:betty, :flintstone=>:wilma}, # :husband=>{:rubble=>:barney, :flintstone=>:fred}} I wrote a purely-functional hoh_merge just for fun. An imperative method would be more efficient (modifying its first argument), h2 = data.inject(Hash.new) { |acc, (hash_key, inner_hash)| inner_hash.keys.inject(acc) { |inner_acc, key| hoh_merge!(inner_acc, key => { hash_key => inner_hash[key] }) } } But here inner_acc is always identical to acc, making the inject just for show. You're back to using #each. I guess the moral of the story is that functional style in ruby realistically applies only to flat arrays and hashes. Once we reach the hash-of-hashes realm, imperative constructs are more suitable. Lazy evaluation is needed to be efficient, recursive, and purely functional all at the same time. (See Haskell.) -- Posted via http://www.ruby-forum.com/.