2016-06-12 2 views
-1

У меня есть массив объектов, который выглядит следующим образом:Рубин цепи два «group_by» методы

[ 
    {day: 'Monday', class: 1, name: 'X'}, 
    {day: 'Monday', class: 2, name: 'Y'}, 
    {day: 'Tuesday', class: 1, name: 'Z'}, 
    {day: 'Monday', class: 1, name: 'T'} 
] 

Я хочу сгруппировать их по дням, а затем классы, т.е.

groupedArray['Monday'] => {'1' => [{name: 'X'}, {name: 'T'}], '2' => [{name: 'Y'}]} 

Я я видел, есть

group_by { |a| [a.day, a.class]} 

Но это создает хэш с ключом [день, класс].

Есть ли способ, которым я могу достичь этого, без необходимости группировать их в первый день, а затем перебирать каждый день и группировать их по классу, а затем подталкивать их к новому хешу?

+0

Вы не хотите, чтобы 'groupedArray ['Monday'] => {'1' => [{name: 'X'}, {name: 'T'}], '2' => [ {name: 'Y'}]} '? Я ожидаю, что это отразится на downvotes. –

+0

Да, это то, чего я хочу. Не заметил неправильных скобок, извините – Dragos

+0

Просто хотел узнать, есть ли более короткий способ сделать это. – Dragos

ответ

2
arr = [ 
    {day: 'Monday', class: 1, name: 'X'}, 
    {day: 'Monday', class: 2, name: 'Y'}, 
    {day: 'Tuesday', class: 1, name: 'Z'}, 
    {day: 'Monday', class: 1, name: 'T'} 
] 

Один из способов получения желаемого хэша заключается в использовании формы Hash#update (он же объединить!), Который использует блок для определения значений ключей, которые присутствуют в обеих хешей быть объединены. Здесь это делается дважды, сначала, когда значения :day одинаковы, то для каждого такого вхождения, когда значения :class одинаковы (при заданном значении :day).

arr.each_with_object({}) { |g,h| 
    h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] }) { |_,h1,h2| 
    h1.update(h2) { |_,p,q| p+q } } } 
    #=> {"Monday" =>{"1"=>[{:name=>"X"}, {:name=>"T"}], "2"=>[{:name=>"Y"}]}, 
    # "Tuesday"=>{"1"=>[{:name=>"Z"}]}} 

Этапы заключаются в следующем.

enum = arr.each_with_object({}) 
    #=> #<Enumerator: [{:day=>"Monday", :class=>1, :name=>"X"}, 
    #     {:day=>"Monday", :class=>2, :name=>"Y"}, 
    #     {:day=>"Tuesday", :class=>1, :name=>"Z"}, 
    #     {:day=>"Monday", :class=>1, :name=>"T"}]:each_with_object({})> 

Мы можем увидеть значения, которые будут генерироваться этим интервьюером путем преобразования его в массив:

enum.to_a 
    #=> [[{:day=>"Monday", :class=>1, :name=>"X"}, {}], 
    # [{:day=>"Monday", :class=>2, :name=>"Y"}, {}], 
    # [{:day=>"Tuesday", :class=>1, :name=>"Z"}, {}], 
    # [{:day=>"Monday", :class=>1, :name=>"T"}, {}]] 

Пустой хеш в каждом массиве является хэш строится и возвращается. Он изначально пуст, но будет частично сформирован по мере обработки каждого элемента из enum.

Первый элемент enum передается к блоку (по Enumerator#each) и блок-переменные присваиваются с помощью параллельного назначения (Somtimes называется множественное присваивание):

g,h = enum.next 
    #=> [{:day=>"Monday", :class=>1, :name=>"X"}, {}] 
g #=> {:day=>"Monday", :class=>1, :name=>"X"} 
h #=> {} 

теперь выполнение блока Расчет:

h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] }) 
    #=> {}.update("Monday"=>{ "1"=>[{name: "X"}] }) 
    #=> {"Monday"=>{"1"=>[{:name=>"X"}]}} 

Эта операция возвращает обновленное значение h, хэш будет построен.

Обратите внимание, что update «s аргумент

"Monday"=>{ "1"=>[{name: "X"}] } 

представляет собой сокращенную

{ "Monday"=>{ "1"=>[{name: "X"}] } } 

Поскольку ключ "Monday" не присутствовал в обоих хешей быть слиты (h не было ключей), блок

{ |_,h1,h2| h1.update(h2) { |_,p,q| p+q } } } 

не использовался для определения ermine значение "Monday".

Теперь следующее значение enum передается в блок и блок переменных назначаются:

g,h = enum.next 
    #=> [{:day=>"Monday", :class=>2, :name=>"Y"}, 
    # {"Monday"=>{"1"=>[{:name=>"X"}]}}] 
g #=> {:day=>"Monday", :class=>2, :name=>"Y"} 
h #=> {"Monday"=>{"1"=>[{:name=>"X"}]}} 

Обратите внимание, что h была обновлена. Теперь мы выполняем расчет блока:

h.update(g[:day]=>{ g[:class].to_s=>[{name: g[:name] }] }) 
    # {"Monday"=>{"1"=>[{:name=>"X"}]}}.update("Monday"=>{ "2"=>[{name: "Y"}] }) 

Оба хэша, объединившиеся, используют ключ «Понедельник». Поэтому мы должны использовать блок, чтобы определить, слитый значение «Понедельник»:

{ |k,h1,h2| h1.update(h2) { |m,p,q| p+q } } } 
    #=> {"1"=>[{:name=>"X"}]}.update("2"=>[{name: "Y"}]) 
    #=> {"1"=>[{:name=>"X"}], "2"=>[{:name=>"Y"}]} 

ВИДЕТЬ док для update для объяснения блока переменных k, h1 и h2 для наружного update и m, p и q для внутреннего update. k и m - значения общего ключа. Поскольку они не используются в блочных вычислениях, я заменил их символами подчеркивания, что является обычной практикой.

Итак:

h #=> { "Monday" => { "1"=>[{ :name=>"X" }], "2"=>[{ :name=>"Y"}] } } 

До этой операции хэш h["Monday] еще не было ключа 2, поэтому второй update не требует использования блока

{ |_,p,q| p+q } 

Этот блок однако, когда последний элемент enum слит в h, так как значения обоих :day и :class одинаковы для двух га shes сливается.

Остальные вычисления аналогичны.

+0

Замечательный ответ, но большинство ваших ответов. –

Смежные вопросы