【问题标题】:Sort a list of objects by using their attributes in Ruby使用 Ruby 中的属性对对象列表进行排序
【发布时间】:2009-05-12 13:14:22
【问题描述】:

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

  1. 具有最高caloriesFruits 最先出现。例如,500 卡路里的水果出现在 400 卡路里的水果之前。

  2. 如果两个Fruits 的calories 相等,则Fruitname 按字母顺序排在第一位,忽略大小写。例如,给定两种热量相等的水果,一种名为“香​​蕉”的水果将排在一种名为“柑橘”的水果之前。

Fruit 的定义不是我能控制的,所以我更喜欢不涉及将任何东西混入Fruit 或更改它的解决方案。这可能吗?

【问题讨论】:

    标签: ruby sorting idioms


    【解决方案1】:

    简单的解决方法是

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

    当然,如果这是水果的规范排序顺序,那么应该使用<=> 方法定义,并将Comparable 模块混合到@ 987654324@

    【讨论】:

    • 这是否考虑到 f.name 应该不区分大小写比较这一事实?
    • 忽略大小写,换入 f.name.downcase
    • 这加上一个 f.name.downcase 正是我所需要的。谢谢! (不,水果没有定义的规范排序顺序;碰巧这就是在这种特殊情况下需要排序的方式。)
    【解决方案2】:

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

    捷径

    Enumerable.sort_by

    正如Gareth 所指出的,Enumerable(包含在数组中)有一个 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 方式

    Array.sort

    来自 Perl 背景,我的第一个冲动是抓住宇宙飞船运算符 。调皮的小恶魔。数组有排序和排序!使其非常有用的方法。此解决方案速度较慢,并且因为它更长,所以更可能引入错误。使用它的唯一原因是,如果您正在与不熟悉 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
    }
    

    【讨论】:

      【解决方案3】:

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

      【讨论】:

      • 'sort' 的问题是它会多次重新评估块,对于每对比较的项目。使用 'sort_by' 计算每个项目的比较键 一次 并对它们进行排序
      • ..虽然已经说过,但在这种情况下不太可能成为问题,因为此示例不会使用缓慢的计算。但是,我仍然认为 sort_by 通常是一种更具可扩展性的解决方案
      【解决方案4】:

      如果您需要对 Fruits 进行大量排序,您可能应该在前面多做一些工作,让您的对象具有可比性。

      为此,您需要实现 Spaceship-Operator (&lt;=&gt;) 并包含 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 还免费为您提供了许多其他方法(==&lt;&gt;),因此您可以执行if (apple &lt; banana) 之类的操作(请参阅the documentation for the Comparable Module 了解更多信息)

      ,如果self 小于other,则指定返回-1,如果other 更小,则返回+1,如果两个对象相等,则返回0

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-11-14
        • 2010-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-27
        相关资源
        最近更新 更多