排序方式:更改多字段排序中一个字段的排序

时间:2016-10-25 19:15:52

标签: ruby-on-rails sorting

当使用ruby的sort_by!进行多字段排序时,如何更改为按其排序的其中一个字段的排序

例如:

a = [{:id => 1, :email_type=>"discover", :date=>2016-10-25 12:27:21 -0400}, {:id => 2, :email_type=>"personal", :date=>2016-10-25 12:27:34 -0400}, {:id => 3, :email_type=>"discover", :date=>2016-10-25 12:27:42 -0400}]

a.sort_by! {|e| [e[:email_type], e[:date]]}

返回

[{:id => 1, :email_type=>"discover", :date=>2016-10-25 12:27:21 -0400}, {:id => 3, :email_type=>"discover", :date=>2016-10-25 12:27:42 -0400}, {:id => 2, :email_type=>"personal", :date=>2016-10-25 12:27:34 -0400}]

按类型排序,然后按日期ASC。但是我想要日期DESC

[{:id => 3, :email_type=>"discover", :date=>2016-10-25 12:27:42 -0400}, {:id => 1, :email_type=>"discover", :date=>2016-10-25 12:27:21 -0400}, {:id => 2, :email_type=>"personal", :date=>2016-10-25 12:27:34 -0400}]

我该怎么做?

1 个答案:

答案 0 :(得分:0)

arr = [{:id => 1, :email_type=>"discover", :date=>"2016-10-25 12:27:21 -0400"},
       {:id => 2, :email_type=>"personal", :date=>"2016-10-25 12:27:34 -0400"},
       {:id => 3, :email_type=>"discover", :date=>"2016-10-25 12:27:42 -0400"}]

这有两种方法。

将日期时间转换为DateTime个对象,转换为秒数并按:email_type排序,负数为秒

require 'date'

arr.sort_by do |h| 
  [h[:email_type], -DateTime.strptime(h[:date], '%Y-%m-%d %H:%M:%S %z').to_time.to_i]
end    
  #=> [{:id=>3, :email_type=>"discover", :date=>"2016-10-25 12:27:42 -0400"},
  #    {:id=>1, :email_type=>"discover", :date=>"2016-10-25 12:27:21 -0400"},
  #    {:id=>2, :email_type=>"personal", :date=>"2016-10-25 12:27:34 -0400"}] 

按所需最终顺序构造:email_type值的数组,按该数组的索引和日期时间字符串排序,然后反向

email_type_order = arr.map { |h| h[:email_type] }.uniq.sort.reverse
  #=> ["personal", "discover"]
arr.sort_by { |h| [email_type_order.index(h[:email_type]), h[:date]] }.reverse
  #=> [{:id=>3, :email_type=>"discover", :date=>"2016-10-25 12:27:42 -0400"},
  #    {:id=>1, :email_type=>"discover", :date=>"2016-10-25 12:27:21 -0400"},
  #    {:id=>2, :email_type=>"personal", :date=>"2016-10-25 12:27:34 -0400"}] 

对于Ruby v2.2 +,您可以使用Enumerable#max_by并使用等于数组大小的参数来避免最终的reverse操作:

email_type_order = arr.map { |h| h[:email_type] }.uniq.sort.reverse
  #=> ["personal", "discover"]
arr.max_by(arr.size) { |h| [email_type_order.index(h[:email_type]), h[:date]] }

请注意,在这里我们可以对日期时间字符串进行排序(即,不将其转换为DateTime对象)因为字符串按年,月,日,小时,分钟,秒和时区排序,字段用零填充。