Ruby中的多条件排序
我有一个Post对象集合,我希望能够根据以下条件对它们进行排序:Ruby中的多条件排序,ruby,sorting,operators,comparison-operators,spacecraft-operator,Ruby,Sorting,Operators,Comparison Operators,Spacecraft Operator,我有一个Post对象集合,我希望能够根据以下条件对它们进行排序: 首先,按类别(新闻、事件、实验室、投资组合等) 然后按日期(如果是日期)或按位置(如果为其设置了特定索引) 一些帖子会有日期(新闻和事件),其他帖子会有明确的位置(实验室和投资组合) 我希望能够调用posts.sort,所以我已经覆盖了,但是我正在寻找根据这些条件进行排序的最有效的方法。下面是一个伪方法: def <=>(other) # first, everything is sorted into
- 首先,按类别(新闻、事件、实验室、投资组合等)
- 然后按日期(如果是日期)或按位置(如果为其设置了特定索引)
posts.sort代码>,所以我已经覆盖了
,但是我正在寻找根据这些条件进行排序的最有效的方法。下面是一个伪方法:
def <=>(other)
# first, everything is sorted into
# smaller chunks by category
self.category <=> other.category
# then, per category, by date or position
if self.date and other.date
self.date <=> other.date
else
self.position <=> other.position
end
end
def(其他)
#首先,每件事都被分类
#按类别划分的小块
自我类别其他类别
#然后,按类别、日期或职位
如果self.date和other.date
self.date其他日期
其他的
自我定位
结束
结束
看起来我实际上必须对两个不同的时间进行排序,而不是将所有内容都塞进一个方法中。类似于sort\u by\u category
,然后sort代码>。最简单的方法是什么?您应该始终按照相同的标准进行排序,以确保有意义的顺序。如果比较两个nil
日期,则位置可以判断顺序,但如果将一个nil
日期与一个设定日期进行比较,则必须决定哪个日期先到,而不考虑位置(例如,通过将nil
映射到过去的日期)
否则,想象一下:
a.date = nil ; a.position = 1
b.date = Time.now - 1.day ; b.position = 2
c.date = Time.now ; c.position = 0
根据您最初的标准,您将有:a
您还希望立即进行排序。对于
实现,请使用#非零?
:
def <=>(other)
return nil unless other.is_a?(Post)
(self.category <=> other.category).nonzero? ||
((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? ||
(self.position <=> other.position).nonzero? ||
0
end
更好的是,还有排序依据
和排序依据代码>可用于构建阵列以比较哪个优先级的内容:
post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] }
使用sort_by
除了较短之外,还有一个优点,即您只能获得有序的标准
注:
排序依据代码>是在Ruby 1.9.2中引入的。你可以把它和老红宝石一起使用
- 我假设
Post
不是ActiveRecord::Base
的子类(在这种情况下,您希望由db服务器完成排序)
或者,您可以在数组中一下子进行排序,唯一的问题是处理其中一个属性为nil的情况,尽管如果您通过选择适当的nil保护知道数据集,仍然可以处理该情况。此外,从您的psuedo代码中也不清楚日期和职位比较是按优先级顺序列出的,还是按优先级顺序列出的(即,如果两个职位都有使用日期,则使用日期)。第一个解决方案假定使用、类别、日期和位置
def <=>(other)
[self.category, self.date, self.position] <=> [other.category, other.date, other.position]
end
def(其他)
[self.category,self.date,self.position][other.category,other.date,other.position]
结束
第二个假设是日期或位置
def <=>(other)
if self.date && other.date
[self.category, self.date] <=> [other.category, other.date]
else
[self.category, self.position] <=> [other.category, other.position]
end
end
def(其他)
如果self.date&&other.date
[self.category,self.date][other.category,other.date]
其他的
[self.category,self.position][other.category,other.position]
结束
结束
谢谢,我不知道数字#非零?
。?
-方法返回非布尔值不是有点奇怪吗?@Mladen:是的,但非常有用。另一个例子是,您可能期望一个true/false
:String
返回nil
,而不是false
。这有点误导:post_ary.sort_by{a,b |(a.category…}
sort_by不接受带有两个参数的块。对于更复杂的排序问题,您应该返回一个数组。。也就是说:post|ary.sort|u by{| a |[a.category,a.date,a.position]}
@TimoLehto:当然,谢谢你指出我的错误。相应地进行了编辑。为什么id为:。非零?| |0当您需要此| | 0Ah时是否有特殊条件,忘记了日期的nil
。这种排序顺序没有很好地排序(请参阅我更新的答案)。就我的学习而言,你说它没有很好地排序是什么意思?对于一个有根据的顺序,以下内容始终适用:a
意味着a
。请参见我的答案,以获取一个与此标准不符的示例。但是,您可以通过对第一种情况进行以下调整来获得该示例:[self.category,self.date | AGES | u AGO,self.position][other.category,other.date | AGES | u AGO,other.position]来使用您的命名,对吗,不那么优雅,但是没有额外的检查结果是一样的,这样使用数组是很好的。但是速度会慢一些,特别是当需要计算某些字段时(例如,#position
是一种进行某些计算的方法),但会产生相同的结果。
def <=>(other)
if self.date && other.date
[self.category, self.date] <=> [other.category, other.date]
else
[self.category, self.position] <=> [other.category, other.position]
end
end