2012-01-17 3 views
20

У меня есть mulitdimensional массив так:Сортировка: Сортировка массива на основе нескольких условий в Рубине

[ 
    [name, age, date, gender] 
    [name, age, date, gender] 
    [..] 
] 

Я интересно, лучший способ сортировки этого массива на основе нескольких условий ... Например, как я бы сортировал на основе возрастпервый, то по названию?

я бездельничал с методом sort следующим образом:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] } 

Кроме того, я не очень понимаю, этот синтаксис, я не получаю результаты, которые я бы ожидать. Должен ли я использовать метод sort? Должен ли я отдельно сравнивать результаты по mapping массиву?

+1

возможно дубликат [Сортировка коллекции объектов по количеству (по убывающей), то по букве (алфавитного)] (Http: // stackoverflow.com/questions/2232470/sort-a-collection-of-objects-by-number-highest-first-then-by-letter-alphabeti) –

+1

@pruett: неуважение к ответу robbrit, но вы должны рассмотреть выбранный ответ, нет ничего плохого в использовании Enumerable # sort * except *, когда Enumerable # sort_by выполняет задание. Это может ввести в заблуждение для людей, приземляющихся здесь. – tokland

+0

Возможный дубликат [Ruby sort by multiple values?] (Http://stackoverflow.com/questions/4309723/ruby-sort-by-multiple-values) – jtbandes

ответ

39

Вы должны всегда использовать sort_by для ключа. Это не только более читаемо, но и много более эффективно. Кроме того, я хотел бы также предпочитают использовать деструктурирующие привязку, опять же, для удобства чтения:

ary.sort_by {|name, age| [age, name] } 
+3

[destructuring bind] (http://stackoverflow.com/a/3952057/38765) –

11

Это должно сделать трюк:

array.sort { |a,b| [ a[1], a[0] ] <=> [ b[1], b[0] ] } 

Так что же это сделать? Он использует много идиом Ruby.

  • Первый - это блоки, которые напоминают обратные вызовы или анонимные функции/классы на других языках. Метод Array sort использует их для сравнения двух элементов на основе возвращаемого значения блока. Вы можете прочитать все о них here.
  • Следующий оператор <=>. Он возвращает -1, если первый аргумент меньше второго, 0, если они равны, и 1, если первый больше второго. Когда вы используете его с массивами, он будет сравнивать элементы массива по отдельности, пока один из них не вернется -1 или 1. Если массивы равны, вы получите 0.
+0

отличное объяснение ... спасибо @robbrit. – pruett

+6

Вы также можете использовать 'sort_by {| a | [a [1], a [0]]} ', построение всех этих массивов не приходит бесплатно. –

6

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


Это работает для меня

people = [ 
     ["bob", 15, "male"], 
     ["alice", 25, "female"], 
     ["bob", 56, "male"], 
     ["dave", 45, "male"], 
     ["alice", 56, "female"], 
     ["adam", 15, "male"] 
    ] 

people.sort{|a,b| (a[1] <=> b[1]) == 0 ? (a[0] <=> b[0]) : (a[1] <=> b[1]) } 

# The sorted array is 

[["adam", 15, "male"], 
["bob", 15, "male"], 
["alice", 25, "female"], 
["dave", 45, "male"], 
["alice", 56, "female"], 
["bob", 56, "male"]] 

Что это делает это сравнение по возрасту первым, и если возраст такой же (< => returs 0), то сравнивая имя.

+0

очень приятно! Мне нравится этот синтаксис – pruett

+0

Что делать, если возраст такой же, и тогда мы хотим отсортировать имя в порядке убывания? – inquisitive

+0

@ Чувствительный тогда в этом случае я думаю, что вы переключили «a» и «b» в «(a [1] <=> b [1])» справа. – user1515295