简体   繁体   中英

Ruby - Order Array of Hashes By Group

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM