11

我想展示一个画廊,products其中包含可供出售和不出售的产品。只有我希望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?返回布尔值的方法对现有的对象列表进行排序,那会更容易。

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

4

4 回答 4

19

当然。理想情况下,我们会使用sort_by!

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

或者更时髦的人

@products.sort_by!(&:on_sale?)

但遗憾的是,<=>它不适用于布尔值(请参阅Why does not sort or the spaceship (flying sauce) operator (<=>) work on booleans in 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 之前,因为您希望"true"值在之前"false"

或者如果您有二级排序:

@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!会修改原始集合的内容,您说这是一个要求。

于 2013-02-11T15:17:08.053 回答
5

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

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

于 2013-02-11T15:30:00.997 回答
5

无需排序:

products_grouped = @products.partition(&:on_sale?).flatten(1)
于 2013-02-11T15:31:38.540 回答
0

可以通过交换“false”和“true”来完成升序和降序

Products.sort_by {|product| product.on_sale? ==  true ? "false" : "true" }
于 2016-07-27T07:20:38.663 回答