Хеши группы Ruby по значению ключа
У меня есть массив, который выводится методом 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
, а затем возвращен как хэш с массивом для каждого типа, любая помощь вообще будет действительно очень полезной, даже некоторые полезные подсказки будет с благодарностью.
Ответы
Ответ 1
array.group_by {|x| x['type']}
или если вы хотите, чтобы ключевые слова символа могли даже
array.group_by {|x| "type_#{x['type']}".to_sym}
Я думаю, что это лучше всего выражает "Итак, каждый массив хэшей будет сгруппирован по значению его ключа ключа, а затем возвращается как хэш с массив для каждого типа", даже если он оставляет ключ :type
только в хэшах вывода.
Ответ 2
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
Примечание: здесь немного разные хеш-ключи, я использовал значения типов непосредственно в качестве ключа
если вам нужно иметь хэш-ключи, как в вашем примере, вы можете добавить строку, которая закомментирована.
P.S.: Я только что увидел решение Tapio - это очень красиво и коротко! Обратите внимание, что он работает только с Ruby >= 1.9
Ответ 3
Что-то вроде этого возможно?
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
Ответ 4
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}