【问题标题】:Is it possible to sort a list of objects depending on the individual object's response to a method?是否可以根据单个对象对方法的响应对对象列表进行排序?
【发布时间】:2013-02-11 15:12:15
【问题描述】:

我想展示一个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 ruby-on-rails-3 sorting activerecord ruby-on-rails-3.2


    【解决方案1】:

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

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

    或时髦的人

    @products.sort_by!(&:on_sale?)
    

    但遗憾的是,&lt;=&gt; 不适用于布尔值(请参阅 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 返回一个新集合,这通常是一种更简洁、不易出错的解决方案,但sort! 修改了原始集合的内容,您说这是一个要求。

    【讨论】:

    • 看起来很棒!但是我的特定代码遇到了问题,也许你可以帮助我。如果我使用单线,我会得到以下错误comparison of FalseClass with true failed,如果我使用你提到的第一个代码,我会得到comparison of [MyModule]::Product with [MyModule]::Product failed。不知道为什么会失败。
    • 一些注意事项: 1) 应尽可能始终使用sort_by 而不是sort(更具声明性、更简洁、更高效)。可以在上面显示的所有 sn-ps 中使用它。 2) 我最好链接到非破坏性Enumerable#sort_by,应尽可能避免就地更新。 3)@product.sort_by(&amp;:on_sale?).
    • 看起来 &lt;=&gt; 不适用于布尔值(在 ruby​​-1.9.3-p327 中)。现在正在研究它......是的,grosser.it/2010/07/30/ruby-true-false-comparison-with 确认它是早期版本
    • 更新了答案,并纳入了 tokland 的 sort_by 建议
    • 我在我的应用程序的不同区域使用您的答案制定了一个解决方案 - 不太优雅,但这篇文章是让我到达那里的原因。标记为答案。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      无需排序:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-01-03
          • 2023-04-01
          • 2022-01-11
          • 2022-01-05
          相关资源
          最近更新 更多