我想展示一个产品的画廊,其中包括出售和不出售的产品.只有我想要出售的产品出现在列表的前面,并且不出售的对象将显示在列表的末尾.
一个简单的方法可以让我完成这个工作就是做两个列表,然后合并它们(一个on_sale?对象列表和一个不是on_sale?对象的列表):
available_products = [] sold_products = [] @products.each do |product| if product.on_sale? available_products << product else sold_products << product end end
. . .但是,对于我现有的应用程序的结构,这将需要过多的重构,因为我的代码很奇怪(我失去了我的分页,我宁愿不重构).如果有办法通过我的产品型号的方法on_sale对现有的对象列表进行排序会更容易吗?它返回一个布尔值.
是否可以通过现有的列表更加优雅地迭代,并通过rails中的真实或虚假值进行排序?我只是问,因为有这么多我不知道隐藏在这个框架/语言(ruby),我想知道他们的工作是否已经在我之前完成.
解决方法
当然.理想情况下,我们将使用
sort_by!
做这样的事情:
@products.sort_by! {|product| product.on_sale?}
或者snazzier
@products.sort_by!(&:on_sale?)
但可悲的是,< =不适用于布尔值(参见Why doesn’t sort or the spaceship (flying saucer) operator (<=>) work on booleans in Ruby?),sort_by不适用于布尔值,因此我们需要使用这个技巧(谢谢rohit89!)
@products.sort_by! {|product| product.on_sale? ? 0 : 1}
如果您想获得优势,则排序方法会占用一个块,并且在该块内,您可以使用任何您喜欢的逻辑,包括类型转换和多个键.尝试这样的东西:
@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放在前面,因为你想要“真”值出现在“假”之前)
或者如果您有次要排序:
@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!
会修改原始集合的内容,这是您要求的.