24

我有一个对象数组

[<#a star=1  val=1>, <#a star=nil val=3> , <#a star=2  val=2>]

我需要按时间排序数组,然后按 val

[ <#a star=2  val=2>, <#a star=1  val=1>, <#a star=nil val=3> ]

但是使用 sort_by 会引发错误,因为时间为零。

我现在正在使用一种丑陋的方式进行排序,但我相信有一个很好的方法来解决它

starred=[]
@answers.each {|a| (starred << a) if a.starred }
@answers=@answers-starred
starred=starred.sort_by {|a| a.starred }.reverse
@answers=starred+@answers
4

3 回答 3

42
starred.sort_by { |a| [a ? 1 : 0, a] }

当它必须比较两个元素时,它会比较一个数组。当 Ruby 比较数组(调用===方法)时,它比较第一个元素,只有当第一个元素相等时才去第二个元素。? 1 : 0保证,我们将 Fixnum 作为第一个元素,所以应该没有错误。

如果你这样做? 0 : 1nil将出现在数组的末尾而不是开头。
这是一个例子:

irb> [2, 5, 1, nil, 7, 3, nil, nil, 4, 6].sort_by { |i| [i ? 1 : 0, i] }
=> [nil, nil, nil, 1, 2, 3, 4, 5, 6, 7]

irb> [2, 5, 1, nil, 7, 3, nil, nil, 4, 6].sort_by { |i| [i ? 0 : 1, i] }
=> [1, 2, 3, 4, 5, 6, 7, nil, nil, nil]
于 2010-12-18T01:10:52.387 回答
8

如果您希望 nil 首先出现(零等效):

@answers.sort_by { |a| a.star or 0 }

如果你想让它们最后出现,你可以用 Max Int 替换零,但这感觉太老套了。这可能会更好:

@answers.select(&:starred?).sort_by(&:star) + @answers.reject(&:starred?)

Nakilon 提供的答案非常出色,尽管您实际上是在两个不同的属性上进行了两次排序。对于大多数情况,它可能就足够了。

于 2013-03-27T18:55:15.790 回答
3

只需使用value.to_i

starred.sort_by { |a| a.to_i }.reverse
于 2017-10-05T12:48:25.693 回答