Ruby групповые хэши по значению ключа - PullRequest
16 голосов
/ 06 октября 2011

У меня есть массив, который выводится методом map / Reduce, выполняемым MongoDB, он выглядит примерно так:

[{"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>299.0}, 
{"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>244.0}, 
{"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>1.0, "count"=>204.0}, 
{"minute"=>45.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>510.0}, 
{"minute"=>45.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>437.0}, 
{"minute"=>0.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>469.0}, 
{"minute"=>0.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>477.0}, 
{"minute"=>15.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>481.0}, 
{"minute"=>15.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>401.0}, 
{"minute"=>30.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>468.0}, 
{"minute"=>30.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>448.0}, 
{"minute"=>45.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>0.0, "count"=>485.0}, 
{"minute"=>45.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "type"=>10.0, "count"=>518.0}] 

Вы заметите, что есть три различных значения для type, в данном случае 0, 1 и 2, теперь нужно сгруппировать этот массив хэшей по значению type ключ, поэтому, например, этот массив будет выглядеть так:

{
  :type_0 => [
    {"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>299.0}, 
    {"minute"=>45.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>510.0}, 
    {"minute"=>0.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>469.0}, 
    {"minute"=>15.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>481.0}, 
    {"minute"=>30.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>468.0}, 
    {"minute"=>45.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>485.0}
  ],

  :type_1 => [
    {"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>204.0}
  ],

  :type_10 => [
    {"minute"=>30.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>244.0}, 
    {"minute"=>45.0, "hour"=>15.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>437.0},
    {"minute"=>0.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>477.0}, 
    {"minute"=>15.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>401.0}, 
    {"minute"=>30.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>448.0}, 
    {"minute"=>45.0, "hour"=>16.0, "date"=>5.0, "month"=>9.0, "year"=>2011.0, "count"=>518.0}
  ]
} 

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

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

Ответы [ 4 ]

31 голосов
/ 06 октября 2011
array.group_by {|x| x['type']}

или если вы хотите, чтобы символьные ключевые вещи вы могли бы даже

array.group_by {|x| "type_#{x['type']}".to_sym}

Я думаю, что это лучше всего выражается "Так что в основном каждый массив хэшей будет сгруппированпо значению его ключа типа , а затем возвращается в виде хэша с массивом для каждого типа", даже если он оставляет ключ :type в выходных хэшах одним.

2 голосов
/ 06 октября 2011

Возможно, что-то вроде этого?

mangled = a.group_by { |h| h['type'].to_i }.each_with_object({ }) do |(k,v), memo|
    tk = ('type_' + k.to_s).to_sym
    memo[tk] = v.map { |h| h = h.dup; h.delete('type'); h }
end

Или, если вам не важно сохранить исходные данные:

mangled = a.group_by { |h| h['type'].to_i }.each_with_object({ }) do |(k,v), memo|
    tk = ('type_' + k.to_s).to_sym
    memo[tk] = v.map { |h| h.delete('type'); h } # Drop the h.dup in here
end
2 голосов
/ 06 октября 2011
by_type = {}

a.each do |h|
   type = h.delete("type").to_s
   # type = ("type_" + type ).to_sym

   by_type[ type ] ||= []
   by_type[ type ] << h      # note: h is modified, without "type" key

end

Примечание: здесь немного другие хеш-ключи, я использовал значения типа непосредственно как ключ

, если вам нужны хеш-ключи, как в вашем примере, вы можете добавить строку, которая являетсязакомментировано.


PS: Я только что увидел решение Тапио - оно очень хорошее и короткое!Обратите внимание, что он работает только с Ruby> = 1.9

1 голос
/ 03 сентября 2015

group_by собирает перечислимое в наборы, сгруппированные по результату блока .Вы не обязаны просто получать значение ключа в этом блоке, поэтому, если вы хотите опустить 'type' в этих наборах, вы можете сделать это, например:

array.group_by {|x| "type_#{x.delete('type').to_i}".to_sym}

Это приведет в точности кчто вы спросили.

Дополнительно: Это немного выходит за рамки вопроса, но если вы хотите сохранить исходный массив, вы должны продублировать каждый объект внутри него.Это сделает трюк:

array.map(&:dup).group_by {|x| "type_#{x.delete('type').to_i}".to_sym}
...