2013-08-16 4 views
6

У меня есть Array of Array s, который я хочу отсортировать по длине до кратчайшего. Я достиг этого достаточно легко с sort_byСортировка массива массивов по длине с тай-брейком

> a = [ [1, 2, 9], 
     [4, 5, 6, 7], 
     [1, 2, 3] ] 
> a.sort_by(&:length).reverse # or a.sort_by {|e| e.length}.reverse 
=> [[4, 5, 6, 7], [1, 2, 3], [1, 2, 9]] 

То, что я хочу, однако, иметь своего рода тай-брейка для списков одинаковой длины. Если длина двух списков одинакова, список, чья последняя запись больше, должна начинаться с. Таким образом, в приведенном выше случае следует переключать [1, 2, 9] и [1, 2, 3].

Мне все равно, когда два списка имеют равную длину и равны последнему элементу, они могут быть в любом порядке, если это произойдет. Я не знаю, как и как я могу добиться этого с помощью рубиновой встроенной сортировки.

+0

Используйте блок, как в примере, в документации: http://www.ruby-doc.org/core-2.0/Enumerable.html#method-i-sort_by – SheetJS

ответ

12

Вы все еще можете сделать это с помощью sort_by, вам просто нужно понимать, что Ruby arrays compare element-by-element:

ичных < => other_ary → -1, 0, +1 или ноль

[.. .]

Сравнение каждого объекта в каждом массиве (с использованием оператора < =>).

Массивы сравниваются по принципу «по элементам»; первые два элемента, которые не равны, будут определять возвращаемое значение для всего сравнения.

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

a.sort_by { |e| [-e.length, -e.last] } 

Это даст вам [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]], что вы «Ищем.

Если вы не используете числа, поэтому трюк «отрицание, чтобы обратить вспять порядок» не будет работать, используйте подход Shaunak's sort.

+0

Отличный ответ, хотя может показаться обычным для свободного Рубисты. –

+0

@BorisStitnicky: Спасибо.Я на самом деле вижу это как решение, основанное на SQL, это в значительной степени простая транслитерация 'порядок по длине desc, last desc' (за исключением того, что SQL-версия будет одинаково хорошо работать со строками, поскольку она не зависит от« отрицания, меняет порядок «трюк». –

+0

Отличный ответ @muistooshort. Из любопытства, есть ли вычислительная разница между тем, как ваше решение будет сравниваться с моим? особенно если массивы слишком велики и сложны? Я не уверен, как sory_by выполняет свой блок, будет ли он сортироваться дважды или, как в моем решении, он просто изменяет сравнение в одном и том же. – Shaunak

3

Там вы идете:

a = [ [1, 2, 9],[4, 5, 6, 7],[1, 2, 3] ] 
a.sort { |a, b| (b.count <=> a.count) == 0 ? (b.last <=> a.last): (b.count <=> a.count) } 

Это должно дать вам:

[[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]] 

Как это работает: мы проходим блок для сортировки функцию, которая сначала проверяет, если длина массива является то же самое, если он не проверяет последний элемент.

+2

Я думаю '(a.length == b.length)' для первого условия будет лучше соответствовать вашему объяснению 'если длина массива такая же'. –

+0

не уверен, что вы имеете в виду, можете ли вы записать его, пожалуйста? – Shaunak

+0

Все, что я предлагаю: '(a.length == b.length)?' Вместо '(b.count <=> a.count) == 0?' –

2

Вы можете использовать

a.sort_by {|i| [i.length, i.last] }.reverse 
# => [[4, 5, 6, 7], [1, 2, 9], [1, 2, 3]] 
Смежные вопросы