data = [{'id' => 1, 'num' => 10},
        {'id' => 1, 'num' => 5},
        {'id' => 1, 'num' => 8},
        {'id' => 2, 'num' => 0},
        {'id' => 2, 'num' => 20},
        {'id' => 2, 'num' => -5},
        {'id' => 5, 'num' => 9},
        {'id' => 5, 'num' => 9}]

I want to order this information by groups of the same id according to the max num associated with each group of id. Then within each group, the num can also be used for further ordering.

data_transformed = 
            [{'id' => 2, 'num' => 20},
            {'id' => 2, 'num' => 0},
            {'id' => 2, 'num' => -5},
            {'id' => 1, 'num' => 10},
            {'id' => 1, 'num' => 8},
            {'id' => 1, 'num' => 5},
            {'id' => 5, 'num' => 9},
            {'id' => 5, 'num' => 9}]

This would also be fine

data_transformed = 
            [[{'id' => 2, 'num' => 20},
            {'id' => 2, 'num' => 0},
            {'id' => 2, 'num' => -5}],

            [{'id' => 1, 'num' => 10},
            {'id' => 1, 'num' => 8},
            {'id' => 1, 'num' => 5}],

            [{'id' => 5, 'num' => 9},
            {'id' => 5, 'num' => 9}]]

How can I do this?

有帮助吗?

解决方案

I'd do :

data = [{'id' => 1, 'num' => 10},
        {'id' => 1, 'num' => 5},
        {'id' => 1, 'num' => 8},
        {'id' => 2, 'num' => 0},
        {'id' => 2, 'num' => 20},
        {'id' => 2, 'num' => -5},
        {'id' => 5, 'num' => 9},
        {'id' => 5, 'num' => 9}]

data_grouped_ordered_by_num = data.group_by { |h| h['id'] }.sort_by { |_,v| v.map { |h| h['num'] }.max }.reverse
# => [[2, [{"id"=>2, "num"=>0}, {"id"=>2, "num"=>20}, {"id"=>2, "num"=>-5}]],
#     [1, [{"id"=>1, "num"=>10}, {"id"=>1, "num"=>5}, {"id"=>1, "num"=>8}]],
#     [5, [{"id"=>5, "num"=>9}, {"id"=>5, "num"=>9}]]]

data_grouped_ordered_by_num.map { |k,v| v.sort_by {|h| -h['num']} }
# => [[{"id"=>2, "num"=>20}, {"id"=>2, "num"=>0}, {"id"=>2, "num"=>-5}],
#     [{"id"=>1, "num"=>10}, {"id"=>1, "num"=>8}, {"id"=>1, "num"=>5}],
#     [{"id"=>5, "num"=>9}, {"id"=>5, "num"=>9}]]

其他提示

data.group_by{ |x| x['id'] }.values.map do |x|
  x.sort_by do |y| 
    -y['num']
  end 
end

#=> [[{"id"=>1, "num"=>10}, {"id"=>1, "num"=>8}, {"id"=>1, "num"=>5}],
 [{"id"=>2, "num"=>20}, {"id"=>2, "num"=>0}, {"id"=>2, "num"=>-5}],  
 [{"id"=>5, "num"=>9}, {"id"=>5, "num"=>9}]]

You can use flatten after that to flat the result which will give you your first desired output:

[{"id"=>1, "num"=>10},
 {"id"=>1, "num"=>8},
 {"id"=>1, "num"=>5},
 {"id"=>2, "num"=>20},
 {"id"=>2, "num"=>0},
 {"id"=>2, "num"=>-5},
 {"id"=>5, "num"=>9},
 {"id"=>5, "num"=>9}]
data.sort_by { |h| [h['id'], -h['num']] } 
  #=> [{'id' => 1, 'num'=>10}, {'id'=>1, 'num'=>8}, {'id'=>1, 'num'=> 5},
  #    {'id' => 2, 'num'=>20}, {'id'=>2, 'num'=>0}, {'id'=>2, 'num'=>-5},
  #    {'id' => 5, 'num'=> 9}, {'id'=>5, 'num'=>9}]

For the second format:

data.sort_by { |h| [h['id'], -h['num']] }.chunk { |h| h['id'] }.map(&:last)
  #=> [[{"id"=>1, "num"=>10}, {"id"=>1, "num"=>8}, {"id"=>1, "num"=> 5}],
  #    [{"id"=>2, "num"=>20}, {"id"=>2, "num"=>0}, {"id"=>2, "num"=>-5}],
  #    [{"id"=>5, "num"=> 9}, {"id"=>5, "num"=>9}]]

Array#sort_by uses Array#<=> for comparisons. Documentation for the latter explains why this produces the desired result.

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top