Мой массив выглядит так:

to_sort = [[1, 27, -3, 1.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0],
           [5, 27, -2, 5.0], [6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0],
           [9, 27, 2, 14.0]]

Я хотел бы отсортировать эти массивы по их второму и третьему значениям в порядке возрастания, но массивы, имеющие отрицательное число для третьего числа, должны быть отсортированы по убыванию и помещены после других массивов.
Результат должен быть примерно таким:

sorted = [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
          [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0],
          [1, 27, -3, 1.0]]

Как сделать так, чтобы его можно было максимально оптимизировать?

3
Matthieu Raynaud de Fitte 4 Сен 2016 в 23:38

4 ответа

Лучший ответ

Насколько я понимаю, когда a[2] >= 0, сортировка выполняется по массиву [a[1], a[2]], а элементы, для которых a[2] < 0 должны быть в конце отсортированного массива и отсортированы по [-a[1], -a[2]] .

biggest_plus_1 = to_sort.map { |a| a[2] }.max + 1
  #=> 3
to_sort.sort_by { |a| a[2] >= 0 ? [0, a[1], a[2]] : [biggest_plus_1, -a[1], -a[2]] }
  #=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
  #    [5, 27, -2, 5.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0],
  #    [1, 27, -3, 1.0]] 

Array # sort и Enumerable # sort_by полагаются на метод Массив # <=> для определения порядка каждой пары сортируемых массивов. Два массива, a и b упорядочены лексикографически, что означает следующее. Если a[0] < b[0], то a меньше, чем b (a < b), или, что эквивалентно, a <=> b #=> -1. Аналогичным образом, если a[0] > b[0], то a больше, чем b (a > b) и a <=> b #=> 1. Если a[0] == b[0], связь нарушается путем сравнения вторых элементов таким же образом, и так далее. Если a меньше, чем b (a.size < b.size), и первые a.size элементы каждого массива равны, a < b. a и b равны тогда и только тогда, когда a <=> b #=> 0.

Поскольку элементы a, для которых a[2] < 0 должны быть помещены в конец отсортированного массива, нам необходимо выполнить сортировку по массивам, первые элементы которых помещают массив в начало или в конец отсортированного массива. По этой причине я сделал первый элемент массива сортировки нулем, когда a[2] >= 0 и biggest_plus_1, когда a[2] < 0, где biggest_plus_1 - наибольшее значение a[2] плюс 1.

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

Обратите внимание, что biggest_plus_1 будет неположительным, если все a[2] < 0, но это не имеет значения, поскольку ни один элемент не будет отсортирован по массиву, первый элемент которого равен нулю.

1
Cary Swoveland 4 Сен 2016 в 23:17

Это трехэтапный подход, хотя я уверен, что есть еще один более краткий ответ.

Сначала мы сортируем значения, в которых третий элемент положительный и / или нулевой:

pos = to_sort.select { |arr| arr[2] >= 0 }.sort_by { |arr| [arr[2], arr[3]] }
=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0]]

Затем мы сортируем значения, в которых третий элемент отрицательный:

neg = to_sort.select { |arr| arr[2] < 0 }.sort_by { |arr| [-arr[2], arr[3]] }

Затем мы объединяем их вместе:

pos + neg
=> [[6, 27, 1, 11.0],
 [7, 27, 1, 12.0],
 [8, 27, 1, 13.0],
 [9, 27, 2, 14.0],
 [2, 27, -2, 2.0],
 [3, 27, -2, 3.0],
 [4, 27, -2, 4.0],
 [5, 27, -2, 5.0],
 [1, 27, -3, 1.0]]
4
Anthony 4 Сен 2016 в 21:11

Сортировка по создает массив и использует его для сортировки значений. Все, что нам нужно сделать, это создать массив, который следует необходимой логике. Для того, чтобы:

to_sort.sort_by do |array|
  [
    array[2] > 0 ? -1 : 1,  # Put all non-negative numbers of ix 2 first.
    array[2].abs,           # Sort by absolute value of ix 2.
    array[3]                # Then sort using ix 3.
  ]
}

Результат:

#=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0], [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0], [1, 27, -3, 1.0]]
1
hirolau 5 Сен 2016 в 08:01

Мой вариант ответа на ваш вопрос:

to_sort.sort_by { |a| a[1].abs; a[2] < 0 ? a[2].abs+1 : a[2] }
#=>[[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [9, 27, 2, 14.0],
# [2, 27, -2, 2.0], [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0],
# [1, 27, -3, 1.0]]

Также, мы можем использовать просто .abs, и это будет выглядеть так:

to_sort.sort_by { |a| a[1].abs; a[2].abs } 

Но таким образом -2 == 2 вернет истину, и результат будет таким:

#=> [[6, 27, 1, 11.0], [7, 27, 1, 12.0], [8, 27, 1, 13.0], [2, 27, -2, 2.0],
# [3, 27, -2, 3.0], [4, 27, -2, 4.0], [5, 27, -2, 5.0], [9, 27, 2, 14.0],
# [1, 27, -3, 1.0]]
1
Alex Holubenko 4 Сен 2016 в 22:05