Как вы сортируете несколько типов в рубине? - PullRequest
2 голосов
/ 06 августа 2011

Предположим, у вас есть массив бегунов:

runners = [al, betty, chris, debby]

И у вас есть метод time, который возвращает время бегунов в гонке.

В случае, когда метод time всегда возвращает число с плавающей запятой, я знаю, что вы можете отсортировать бегунов по времени, используя sort_by следующим образом:

runners.sort_by do |runner|
    runner.time
end

Однако предположим, что time иногда возвращает строку, скажем "disqualified". В таком случае, как бы вы отсортировали бегунов по времени с последними дисквалифицированными бегунами? Что, если вы хотите, чтобы сначала были дисквалифицированные участники или в какой-то другой позиции?

Ответы [ 5 ]

5 голосов
/ 07 августа 2011

Вот простой способ.Просто имейте два поля сортировки в массиве, и Ruby будет сортировать по первому критерию, а затем второму.

runners.sort_by do |runner|
    [(runner.time == 'disqualified') ? 1 : 0, runner.time]
end

Здесь 1 будет отсортировано после 0, поэтому дисквалифицированные времена будут последними.

1 голос
/ 06 августа 2011

Вы можете создать собственный класс для этого:

class RunningTime
  attr_reader :time

  def initialize time
    @time = time
  end

  def disqualified?
    @time == 'disqualified'
  end

  def <=> rhs
    case [disqualified?, rhs.disqualified?]
    when [false, true]
      -1
    when [true, true]
      0
    when [true, false]
      1
    else
      time <=> rhs.time
    end
  end
end

runners.sort_by {|runner| RunningTime.new(runner.time)}
0 голосов
/ 08 августа 2011

Предполагая, что вы бегуны пришли из базы данных, использование отдельного поля / метода для проверки дисквалифицированных бегунов позволяет перенести логику сортировки в слой базы данных, что будет быстрее, чем загрузка всех записей в память и сортировкуих в рубине.

0 голосов
/ 07 августа 2011

Вы можете сделать это аналогично этому:

[5,4,"disqualified",2,7].sort_by { |x| x.kind_of?(String) ? Float::INFINITY : x } 
#=> [2, 4, 5, 7, "disqualified"]

В Ruby <1.9 нет <code>Float::INFINITY, но вы можете получить его, выполнив Inf = 1.0 / 0.

0 голосов
/ 06 августа 2011

Время возвращается Object.time.to_f? Если это так, вызов to_f для String вернет 0.0. Если нет, попробуйте вызвать runner.time.to_f в блоке. Все значения времени типа String будут 0.0.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...