Вопрос

У меня есть коллекция пост объектов, и я хочу иметь возможность сортировать их на основе этих условий:

  • Во-первых, по категориям (новостями, событиями, лабораториями, портфолио и т. Д.)
  • Затем по дате, если дата или позицией, если определенный индекс был установлен для него

Некоторые посты будут иметь даты (новости и события), другие будут иметь явные позиции (лаборатории и портфель).

Я хочу быть в состоянии позвонить 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!. Отказ Какой самый рубливый способ сделать это?

Это было полезно?

Решение

Вы всегда должны сортировать по тем же критериям для обеспечения значимого порядка. Если сравнивать два 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 <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

Если вы используете свои критерии сравнения только один раз, или если эти критерии не универсальны и, таким образом, не хотите определять <=>, вы могли бы использовать sort с блоком:

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

Лучше еще, есть sort_by и 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' использовать его со старыми рубинами.
  • Я предполагаю, что Post не подкласс ActiveRecord::Base (в этом случае вы хотите, чтобы сорт был сделан сервером БД).

Другие советы

В качестве альтернативы вы можете сделать то, что в одном падающем массиве в массиве, единственным GOTCHA обрабатывает случай, когда один из атрибутов является Nil, хотя это все еще можно обрабатывать, если вы знаете данные, установленные, выбрав соответствующий охранник Nil. Также из вашего PSuedo Codeo не ясно, если сравнения даты и положения перечислены в порядке приоритета или одно или другое (т. Е. Дата использования, если существует как для положения использования как иначе). Первое решение предполагает использование, категория, а затем дату, а затем позиция

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
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top