8

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

その 1 つのメソッドにすべてを詰め込むのではなく、実際には 2 つの別々の時間をソートする必要があるようです。のようなものsort_by_categoryですsort!。これを行うための最もルビーな方法は何ですか?

4

2 に答える 2

12

意味のある順序を確保するために、常に同じ基準で並べ替える必要があります。2 つのnil日付を比較する場合positionは が順序を判断しても問題ありませんが、1 つの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 < a になります。さて、一番小さいのは??

また、並べ替えを一度に実行したいと考えています。<=>実装には、次を使用します#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

比較基準を 1 回だけ使用する場合、またはその基準が普遍的ではないため を定義したくない場合は、ブロックで<=>使用できます。sort

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

さらに良いことに、どの優先順位で何を比較するかの配列を作成するために使用できるsort_byand which があります。sort_by!

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

を使用すると、短いsort_byだけでなく、適切に順序付けられた基準しか取得できないという利点があります。

ノート:

  • sort_by!Ruby 1.9.2 で導入されました。require 'backports/1.9.2/array/sort_by'古い Ruby で使用できます。
  • 私はそれPostがのサブクラスではないと仮定してActiveRecord::Baseいます(その場合、並べ替えはdbサーバーによって行われる必要があります)。
于 2010-04-14T00:54:40.997 に答える
4

または、配列内で一挙に並べ替えを行うこともできます。唯一の落とし穴は、属性の 1 つが 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
于 2010-04-14T06:27:02.643 に答える