Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Ruby: Общие вопросы > Сортировка по нескольким параметрам


Автор: Bikutoru 3.12.2006, 16:06
Добрый день!
У меня возникла задача: необходимо "вытащить" из текста список слов, и вывести их, предварительно отсортировав по частоте вхождения (если несколько слов имеют одинаковую частоту, то они должны быть отсортированы по алфавиту). Вот с последней частью у меня и возникла проблема. 
На первый взгляд, ничего сложного в этом нет, берем слова, записываем их хеш, где в качестве ключа используется слово, а в качестве значения частота его вхождения и пишем в стиле a-la PERL что-то вроде
Код

# пример входных данных
DATA = {
  'hello' => 20, 
  'foo'   =>  2,
  'world' => 10, 
  'and'   => 25, 
  'you'   =>  5,
  'or'    => 25, 
  'how'   => 10,
  'are'   =>  5,
}

DATA.sort { |a, b| 
  -(a[1] <=> b[1]) || (a[0] <=> b[0])
}.each { |pair| printf "%03d: %s\n", pair[1], pair[0] }

и получаем... правильно, не совсем то, что нужно. Выведенные данные действительно отсортированы по частоте вхождения, но вот с алфавитом имеются определённые проблемы. Сижу, разбираюсь и прихожу к выводу, что 0 есть истина (о чём, кстати, в "Programming Ruby" честно написано - "Any value that is not nil or the constant false is true")! 
Хорошо, тогда на мне  ум приходит другая "гениальная" мысль - а давайте-ка использовать побитовое ИЛИ, что гарантировано даст возможность вычисления всех "сравнений". Да, но ведь в этом случае мы можем получитьвыражение 1 | -1, которое даст совсем не тот результат, который нужен. Следовательно, этот вариант тоже не подходит. В результате я пришел в селдующему варианту:
Код

class Array
  def compare_result
    self.find { |condition| not condition.zero? } || 0
  end
end

DATA.sort { |a, b| 
  [-(a[1] <=> b[1]), a[0] <=> b[0]].compare_result 
}.each { |pair| printf "%03d: %s\n", pair[1], pair[0] }

Ну и, наконец, озвучу свой вопрос - а не дурак ли я нет ли более простого варианта для решения задачи сортировки по нескольким условиям сразу? Меня не оставляет мысль, что это должно решаться как-то проще...

Автор: Pete 3.12.2006, 21:56
Код

DATA.sort { |a, b| 
  a[1] < b[1] ? 1 : a[1] > b[1] ? -1 : a[0] <=> b[0]
}.each { |pair| printf "%03d: %s\n", pair[1], pair[0] }

Автор: Bikutoru 4.12.2006, 12:49
Pete, как увидел твое решение, сразу пришло на ум небольшое упрощение:
Код

DATA.sort { |a, b| 
  (a[1] == b[1]) ? (a[0] <=> b[0]) : -(a[1] <=> b[1]) 
}.each { |pair| printf "%03d: %s\n", pair[1], pair[0] }

Спасибо, именно такой взгляд со стороны и нужен был :-)

Автор: FunnyFalcon 5.12.2006, 12:07
??????????
Код

DATA.sort_by {|v| [v[1], v[0]] }.each {|pair| printf "%03d: %s\n", pair[1], pair[0] }

Автор: V.A.KeRneL 10.12.2006, 13:21
Цитата(FunnyFalcon @ 5.12.2006,  12:07)
??????????
Код

DATA.sort_by {|v| [v[1], v[0]] }.each {|pair| printf "%03d: %s\n", pair[1], pair[0] }

Минусик забыли! smile
Вот так будет, что надо: 
Код

DATA.sort_by {|v| [-v[1], v[0]] }.each {|pair| printf "%03d: %s\n", pair[1], pair[0] }


Моё стандартное решение с использованием метода `nonzero?': 
Код

DATA.sort { |a, b| 
    (-(a[1] <=> b[1])).nonzero? || (a[0] <=> b[0])
}.each { |pair| printf "%03d: %s\n", pair[1], pair[0] }


Автор: Bikutoru 12.12.2006, 12:31
FunnyFalcon, V_A_KeRneL, спасибо за предложенные решения. Мне они понравились гораздо больше...

Добавлено @ 12:32 
Правда проверить раньше выходных на реальных данных не получится...

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)