Ruby group hashes by value of key

大憨熊 提交于 2019-12-05 10:47:59

问题


I have an array, which is output by a map/reduce method performed by MongoDB, it looks something like this:

[{"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}] 

You'll notice that there are three distinct values for type, in this case 0, 1, and 2, now want to do is group this array of hashes by the value its type key, so for example this array would end out looking like:

{
  :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}
  ]
} 

so I know these example arrays are really large, but I think it may be a more simple problem than I'm making it out to be

So basically each array of hashes would be grouped by the value of its type key, and then returned as a hash with an array for each type, any help at all would be really really helpful, even just some helpful hints would be greatly appreciated.


回答1:


array.group_by {|x| x['type']}

or if you want the symbol key things you could even

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

I think this best expresses "So basically each array of hashes would be grouped by the value of its type key, and then returned as a hash with an array for each type", even if it leaves the :type key alone in the output hashes.




回答2:


Something like this perhaps?

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

Or if you don't care about preserving the original data:

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



回答3:


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

Note: slightly different hash keys here, i used the type values directly as the key

if you have to have the hash-keys as in your example, you can add the line that is commented out.


P.S.: I just saw Tapio's solution -- it is very nice and short! Note that it only works with Ruby >= 1.9




回答4:


group_by collects an enumerable into sets, grouped by the result of a block. You are not constrained to simply get the key's value in this block, so if you would like to omit the 'type' in those sets you can do it, like in:

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

This will result exactly into what you asked.

Advanced: This goes a little out of scope of the question, but if you want to preserve the original array, you must duplicate every object inside it. This will do the trick:

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


来源:https://stackoverflow.com/questions/7670043/ruby-group-hashes-by-value-of-key

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!