Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ruby multiple group_by of array

Tags:

ruby

hash

I have an array like this:

[{:a=>"2017-01-01", :b=>"2", :c=>"1"}, {:a=>"2017-01-01", :b=>"2", :c=>"2"}, {:a=>"2017-01-02", :b=>"5", :c=>"1"}]

If, for example, I group the array with the key ":a", the result is:

p = y.group_by { |g| g[:a]}

 => {"2017-01-01"=>[{:a=>"2017-01-01", :b=>"2", :c=>"1"}, {:a=>"2017-01-01", :b=>"2", :c=>"2"}], "2017-01-02"=>[{:a=>"2017-01-02", :b=>"5", :c=>"1"}]} 

Now I want to group for each ":a" the key ":b" like:

 => {"2017-01-01"=>["2" => [{:a => "2017-01-01", :b => "2", :c => "1" }], ... }

How can I do? Thanks in advance.

Edit:

multiple group by ... 1° group_by a, 2° group_by b the result of 1°, etc..

y.group_by {|g| g[:a]}.map do |k,v|
 [
  k, v.group_by { |d| d[:b] }.map do |p,q|
   [p, q.group_by { |f| f[:c] }]
  end.to_h
 ]
end.to_h  
like image 443
sirion1987 Avatar asked Dec 23 '22 20:12

sirion1987


1 Answers

Quick answer, using your code above:

y = [{:a=>"2017-01-01", :b=>"2", :c=>"1"}, {:a=>"2017-01-01", :b=>"2", :c=>"2"}, {:a=>"2017-01-02", :b=>"5", :c=>"1"}]
p = y.group_by { |g| g[:a]}
q = p.map {|date, list| [date, list.group_by {|g| g[:b]}]}.to_h

This gives the desired result of:

q == {
  "2017-01-01" => {
    "2"=> [
      {:a=>"2017-01-01", :b=>"2", :c=>"1"},
      {:a=>"2017-01-01", :b=>"2", :c=>"2"}
    ]
  },
  "2017-01-02" => {
    "5"=> [
      {:a=>"2017-01-02", :b=>"5", :c=>"1"}
    ]
  }
}

This slightly odd pattern of mapping the hash to a list of arrays, then converting it back to a hash (using to_h) can be simplified slightly if you are working with a Rails v4.2+ project, by use of Hash#transform_values:

p.transform_values {|list| list.group_by {|g| g[:b]}}

However with that said, it is generally considered bad practice to be manipulating complex, nested hash objects like this. If your code gets too complicated, you may wish to consider redesigning how it works, in a more Object-Oriented manner.

like image 180
Tom Lord Avatar answered Jan 08 '23 07:01

Tom Lord