2013-02-11 58 views
10

我想展示一个products的画廊,其中包含既可供出售又可出售的产品。我只希望待售的products出现在列表的前面,而不出售的对象将出现在列表的末尾。根据单个对象对某个方法的响应,是否可以对对象列表进行排序?

对我来说,做到这一点的一个简单方法是使两个列表,然后将它们合并(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 

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

是否有可能更优雅地遍历现有的列表,并通过这个真正的或错误的值在轨道排序它?我只问,因为有太多我不知道隐藏在这个框架/语言(红宝石),我想知道他们的工作是否已经完成在我面前。

回答

11

当然。理想情况下,我们会使用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} 

如果您想要更有趣,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返回一个ew collection,这通常是一个更干净,更不容易出错的解决方案,但sort!会修改原始集合的内容,这是您的要求。

+0

看起来不错!但是我用我的特定代码遇到了问题,也许你可以帮助我。如果我使用单行命令,我会得到以下错误:“FalseClass与true failed的比较”,如果我使用您提到的第一个代码,则会得到[MyModule] :: Product与[MyModule] :: Product的比较失败'。不知道为什么这会失败。 – Ecnalyr 2013-02-11 15:26:22

+0

一些注意事项:1)应尽可能使用'sort_by'而不是'sort'(更具说明性,更简洁,更高效)。可以在上面显示的所有代码片段中使用它。 2)我最好链接到非破坏性的'Enumerable#sort_by',只要有可能就应该避免就地更新。 3)'@ product.sort_by(&:on_sale?)'。 – tokland 2013-02-11 15:28:50

+0

看起来像'<=>'不适用于布尔值(ruby-1.9.3-p327)。现在看看它...是的,http://grosser.it/2010/07/30/ruby-true-false-comparison-with/确认它为较早的版本 – AlexChaffee 2013-02-11 15:39:56

4

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

这是我做过什么,当我不得不基于布尔值排序。

4

无需排序:

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

上升和下降可以通过改变间做 “假” 与 “真”

Products.sort_by {|product| product.on_sale? == true ? "false" : "true" } 
相关问题