Ruby 中按多个条件排序

发布于 2024-08-28 18:03:44 字数 714 浏览 11 评论 0原文

我有一个 Post 对象的集合,我希望能够根据以下条件对它们进行排序:

  • 首先,按类别(新闻、事件、实验室、投资组合等)
  • 然后按日期(如果是日期)或按位置(如果是)为其设置了特定索引

一些帖子将有日期(新闻和事件),其他帖子将有明确的职位(实验室和投资组合)。

我希望能够调用 posts.sort!,因此我重写了 <=>,但我正在寻找按这些条件排序的最有效方法。下面是一个伪方法:

def <=>(other)
  # first, everything is sorted into 
  # smaller chunks by category
  self.category <=> other.category

  # then, per category, by date or position
  if self.date and other.date
    self.date <=> other.date
  else
    self.position <=> other.position
  end
end

似乎我必须实际上对两次单独的时间进行排序,而不是将所有内容都塞进该一个方法中。类似sort_by_category,然后sort!。最红宝石的方法是什么?

I have a collection of Post objects and I want to be able to sort them based on these conditions:

  • First, by category (news, events, labs, portfolio, etc.)
  • Then by date, if date, or by position, if a specific index was set for it

Some posts will have dates (news and events), others will have explicit positions (labs, and portfolio).

I want to be able to call posts.sort!, so I've overridden <=>, but am looking for the most effective way of sorting by these conditions. Below is a pseudo method:

def <=>(other)
  # first, everything is sorted into 
  # smaller chunks by category
  self.category <=> other.category

  # then, per category, by date or position
  if self.date and other.date
    self.date <=> other.date
  else
    self.position <=> other.position
  end
end

It seems like I'd have to actually sort two separate times, rather than cramming everything into that one method. Something like sort_by_category, then sort!. What is the most ruby way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

﹏雨一样淡蓝的深情 2024-09-04 18:03:44

您应该始终按照相同的标准进行排序,以确保顺序有意义。如果比较两个 nil 日期,position 可以判断顺序,但如果比较一个 nil 日期与一组日期,你必须决定哪个先执行,无论位置如何(例如,将 nil 映射到过去的一天)。

否则想象一下以下情况:

a.date = nil                   ; a.position = 1
b.date = Time.now - 1.day      ; b.position = 2
c.date = Time.now              ; c.position = 0

根据您最初的标准,您将拥有: a < b< c<一个。那么,哪一个是最小的呢?

您还想立即进行排序。对于您的 <=> 实现,请使用 #nonzero?

def <=>(other)
  return nil unless other.is_a?(Post)
  (self.category <=> other.category).nonzero? ||
  ((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? ||
  (self.position <=> other.position).nonzero? ||
  0
end

如果您仅使用比较条件一次,或者该条件不通用,因此不希望要定义 <=>,您可以将 sort 与块一起使用:

post_ary.sort{|a, b| (a.category <=> ...).non_zero? || ... }

更好的是,有 sort_bysort_by!< /code> 您可以使用它来构建一个数组,以比较哪个优先级:

post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] }

除了更短之外,使用 sort_by 的优点是您只能获得有序的条件。

注意:

  • sort_by! 是在 Ruby 1.9.2 中引入的。您可以要求'backports/1.9.2/array/sort_by'来使用与较老的红宝石一起使用。
  • 我假设 Post 不是 ActiveRecord::Base 的子类(在这种情况下,您希望由数据库服务器完成排序)。

You should always sort by the same criteria to insure a meaningful order. If comparing two nil dates, it is fine that the position will judge of the order, but if comparing one nil date with a set date, you have to decide which goes first, irrespective of the position (for example by mapping nil to a day way in the past).

Otherwise imagine the following:

a.date = nil                   ; a.position = 1
b.date = Time.now - 1.day      ; b.position = 2
c.date = Time.now              ; c.position = 0

By your original criteria, you would have: a < b < c < a. So, which one is the smallest??

You also want to do the sort at once. For your <=> implementation, use #nonzero?:

def <=>(other)
  return nil unless other.is_a?(Post)
  (self.category <=> other.category).nonzero? ||
  ((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? ||
  (self.position <=> other.position).nonzero? ||
  0
end

If you use your comparison criteria just once, or if that criteria is not universal and thus don't want to define <=>, you could use sort with a block:

post_ary.sort{|a, b| (a.category <=> ...).non_zero? || ... }

Better still, there is sort_by and sort_by! which you can use to build an array for what to compare in which priority:

post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] }

Besides being shorter, using sort_by has the advantage that you can only obtain a well ordered criteria.

Notes:

  • sort_by! was introduced in Ruby 1.9.2. You can require 'backports/1.9.2/array/sort_by' to use it with older Rubies.
  • I'm assuming that Post is not a subclass of ActiveRecord::Base (in which case you'd want the sort to be done by the db server).
晨与橙与城 2024-09-04 18:03:44

或者,您可以在数组中一次性进行排序,唯一的问题是处理其中一个属性为 nil 的情况,尽管如果您通过选择适当的 nil 保护了解数据集,仍然可以处理这种情况。此外,从您的伪代码中不清楚日期和位置比较是否按优先顺序或一个或另一个列出(即,如果存在其他使用位置,则使用日期)。第一个解决方案假设用途、类别,后跟日期,后跟位置

def <=>(other)
    [self.category, self.date, self.position] <=> [other.category, other.date, other.position]
end

第二个假设它是日期或位置

def <=>(other)
    if self.date && other.date
        [self.category, self.date] <=> [other.category, other.date]
    else
        [self.category, self.position] <=> [other.category, other.position]
    end
end

Alternatively you could do the sort in one fell swoop in an array, the only gotcha is handling the case where one of the attributes is nil, although that could still be handled if you knew the data set by selecting the appropriate nil guard. Also it's not clear from your psuedo code if the date and position comparisons are listed in a priority order or an one or the other (i.e. use date if exists for both else use position). First solution assumes use, category, followed by date, followed by position

def <=>(other)
    [self.category, self.date, self.position] <=> [other.category, other.date, other.position]
end

Second assumes it's date or position

def <=>(other)
    if self.date && other.date
        [self.category, self.date] <=> [other.category, other.date]
    else
        [self.category, self.position] <=> [other.category, other.position]
    end
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文