Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a nested hash from a sorted array in Ruby-- recursive group_by

Tags:

ruby

I have an array of objects that has been sorted according to several properties of these objects. In order of priority, these properties are foo, bar and baz. This means that the objects are first sorted by foo; then subsequences having the same foo value are sorted by bar; and then those with the same foo and bar values are sorted by baz.

I would like to turn this into a nested hash that reflects this grouping. Basically I'm looking for a recursive Enumerable#group_by. The keys would be values of foo, bar, and baz; the values would be either sub-hashes or arrays of the objects. Here is an example:

[obj1, obj2, ... objn].group_by_recursive(:foo, :bar, :baz)
#=> {
      foo_val_1 => {
        bar_val_1 => {
          baz_val_1 => [
            obj1,
            obj2,
            obj3
          ],
          baz_val_2 => [
            obj4,
            obj5
          ]
        },
        bar_val_2 => {
          baz_val_1 => [
            obj6,
            obj7
          ],
          baz_val_2 => [
            obj8
          ]
        },
      },
      foo_val_2 => {
        ...
      },
      ...
    }
like image 275
Sean Mackesey Avatar asked Mar 11 '13 10:03

Sean Mackesey


2 Answers

Came up with a pretty good solution. Monkey-patch Enumerable like this:

module Enumerable

  def group_by_recursive(*props)
    groups = group_by(&props.first)
    if props.count == 1
      groups
    else
      groups.merge(groups) do |group, elements|
        elements.group_by_recursive(*props.drop(1))
      end
    end
  end

end

The properties you pass can be either Procs or Symbols

like image 175
Sean Mackesey Avatar answered Sep 22 '22 20:09

Sean Mackesey


Similar to Sean's and lacking error handling ...

class Array
  def nested_group_by(*keys)
    return self if keys.length == 0
    groups = group_by(&keys.shift)
    Hash[groups.map { | k, v | [k, v.nested_group_by(*keys)] }]
  end
end
like image 1
undur_gongor Avatar answered Sep 20 '22 20:09

undur_gongor