使用Ruby中的属性对对象列表进行排序

时间:2009-05-12 13:14:23

标签: ruby sorting idioms

我有一个名为Fruit的{​​{1}}结构列表。每个basket结构都有Fruit(字符串)和name(整数)。我想对calories进行排序,以便:

  1. 首先出现basket Fruit calories。例如,含400卡路里的水果出现在400卡路里的水果之前。

  2. 如果两个Fruit具有相同的calories,则Fruit按字母顺序排在第一位name,忽略大小写。例如,给定两个具有相同卡路里的水果,一个名为“香蕉”的水果将出现在一个名为“柑橘”的水果之前。

  3. Fruit的定义不是我控制的,所以我更喜欢不涉及将任何内容混合到Fruit或更改它的解决方案。这可能吗?

4 个答案:

答案 0 :(得分:12)

简单的解决方案是

basket.sort_by { |f| [-f.calories, f.name] }

当然,如果这是 水果的规范排序顺序,那么使用<=>方法和Comparable进行定义模块混合到Fruit

答案 1 :(得分:2)

我们假设您的购物篮是一个数组或其子类。

快速方式

Enumerable.sort_by

正如Gareth所指出的,Enumerable(包含在Array中)有一个sort_by方法,该方法遍历每个列表项一次。一旦你掌握它,它就会更快地运行并且写得更快。

# -f.calories to sort descending
# name.downcase to do a case-insensitive sort
basket = basket.sort_by { |f| [-f.calories, f.name.downcase] }

Perl Way

Array.sort

来自Perl背景,我的第一个冲动就是抓住宇宙飞船运营商&lt; =&gt;。厚脸皮的小恶魔。数组有排序和排序!使它非常有用的方法。这个解决方案速度较慢,因为它的时间越长,就越有可能引入错误。使用它的唯一原因是,如果您正在与不熟悉Ruby且不愿在StackOverflow上找到正确方式的人打交道。

baseket.sort! { |a,b|
  if a.calories == b.calories
    a.name.downcase <=> b.name.downcase
  else
    # Reverse the result to sort highest first.
    -(a.calories <=> b.calories)
  end
}

答案 2 :(得分:1)

请参阅Array#sortAPI doc)。给定两个Fruit对象,您可以传入一个返回-1,0或1的块,您的块可以使用您喜欢的任何属性来确定这些值。

答案 3 :(得分:1)

如果你需要经常对Fruit进行分类,你可能应该先做一些工作并使你的对象具有可比性。

为此,您需要实施Spaceship-Operator(<=>)并包含Comparable。

class Fruit
  attr_accessor :name, :color

  def <=>(other)
    # use Array#<=> to compare the attributes
    [self.name.downcase, self.color] <=> [other.name.downcase, other.color]
  end

  include Comparable
end

然后你就可以做到:

list_of_fruits.sort

Comparable还免费为您提供了许多其他方法(==<>),因此您可以执行if (apple < banana)之类的操作(请参阅the documentation for the Comparable Module了解更多信息)

指定

&lt; =&gt;,如果-1小于self则返回other+1如果other小且{{1}如果两个对象都相等。