0

I have an array of

  • shop objects

    • which belong to city objects

      • which belong to prefecture objects

I'd like to end up with a hash listed by prefecture, then city, then frequency...

I came up with this, but it feels really un-rubylike..

city_by_prefecture = shop_list.reduce({}){ |h,e|
  if h[e.prefecture.name].nil?
    h[e.prefecture.name] = {e.city.name => 1}
  elsif h[e.prefecture.name][e.city.name].nil?
    h[e.prefecture.name][e.city.name] = 1
  else
    h[e.prefecture.name][e.city.name] += 1
  end
  h
}

There must be a DRY-er way to do this !

Andrew Grimm
  • 74,534
  • 52
  • 194
  • 322
minikomi
  • 8,133
  • 3
  • 40
  • 48
  • possible duplicate of [How to assign hash\["a"\]\["b"\]= "c" if hash\["a"\] doesn't exist?](http://stackoverflow.com/questions/5878529/how-to-assign-hashab-c-if-hasha-doesnt-exist) – Andrew Grimm Aug 02 '11 at 08:00
  • 1
    You may want to use `h.has_key?(e.prefecture.name)` rather than `h[e.prefecture.name].nil?`, because that way it's more obvious what you're asking. Also, use `each_with_object` rather than `reduce`, so you don't have to put `h` at the end of the block. – Andrew Grimm Aug 02 '11 at 08:04
  • Thank you Andrew. I wasn't aware of `each_with_object`. – minikomi Aug 03 '11 at 05:35

2 Answers2

1
city_by_prefecture = shop_list.each_with_object({}){ |e,h|
  h[e.prefecture.name] ||= Hash.new(0)
  h[e.prefecture.name][e.city.name] += 1
}
Mladen Jablanović
  • 42,480
  • 10
  • 91
  • 112
0
shops = [
  OpenStruct.new(:prefacture => "pre1", :city => "city1"), 
  OpenStruct.new(:prefacture => "pre1", :city => "city1"), 
  OpenStruct.new(:prefacture => "pre1", :city => "city2"), 
  OpenStruct.new(:prefacture => "pre2", :city => "city3"),
]

counts = Hash[shops.group_by(&:prefacture).map do |prefacture, shops_in_prefacture| 
  [prefacture, Hash[shops_in_prefacture.group_by(&:city).map do |city, shops_in_city| 
    [city, shops_in_city.size]
   end]] 
end]
# {"pre1"=>{"city1"=>2, "city2"=>1}, "pre2"=>{"city3"=>1}}
tokland
  • 63,578
  • 13
  • 136
  • 167