是否可以根据单个对象对方法的响应对对象列表进行排序?

我想展示一个产品库,其中包括出售和非出售的产品。 只有我希望待售的products出现在列表的前面,而非待售的对象才会出现在列表的末尾。

我完成此任务的一个简单方法是创建两个列表,然后合并它们(一个on_sale?对象列表和一个not on_sale?对象列表):

 available_products = [] sold_products = [] @products.each do |product| if product.on_sale? available_products << product else sold_products << product end end 

。 。 。 但是对于我现有应用程序的结构,这需要进行过多的重构,因为我的代码很奇怪(我失去了我的分页,我宁愿不重构)。 如果有一种方法可以通过我的product模型的on_sale?方法对现有的对象列表进行排序,那会更容易on_sale? 它返回一个布尔值。

是否可以更优雅地遍历现有列表并通过rails中的true或false值对其进行排序? 我只是问,因为有很多我不知道隐藏在这个框架/语言(ruby)中,我想知道他们是否在我之前完成了工作。

当然。 理想情况下,我们使用sort_by!做这样的事情sort_by!

 @products.sort_by! {|product| product.on_sale?} 

或者是流氓

 @products.sort_by!(&:on_sale?) 

但遗憾的是, <=>不适用于布尔值(请参阅为什么不排序或太空船(飞碟)运算符(<=>)在Ruby中使用布尔值? )并且sort_by不适用于布尔值,所以我们需要使用这个技巧(谢谢rohit89!)

 @products.sort_by! {|product| product.on_sale? ? 0 : 1} 

如果你想变得更好, sort方法需要一个块,在那个块里面你可以使用你喜欢的任何逻辑,包括类型转换和多个键。 尝试这样的事情:

 @products.sort! do |a,b| a_value = a.on_sale? ? 0 : 1 b_value = b.on_sale? ? 0 : 1 a_value <=> b_value end 

或这个:

 @products.sort! do |a,b| b.on_sale?.to_s <=> a.on_sale?.to_s end 

(将b放在a之前,因为你想要"false"之前的"false" "true"值)

或者如果您有二级排序:

 @products.sort! do |a,b| if a.on_sale? != b.on_sale? b.on_sale?.to_s <=> a.on_sale?.to_s else a.name <=> b.name end end 

请注意, sort返回一个新的集合,这通常是一个更简洁,更不容易出错的解决方案,但sort! 修改原始集合的内容,您说这是一个要求。

@products.sort_by {|product| product.on_sale? ? 0 : 1}

当我不得不根据布尔值进行排序时,这就是我所做的。

无需排序:

 products_grouped = @products.partition(&:on_sale?).flatten(1) 

上升和下降可以通过相互改变“假”和“真”来完成

 Products.sort_by {|product| product.on_sale? == true ? "false" : "true" }