Matthieu Raynaud de Fitte Matthieu Raynaud de Fitte - 3 months ago 18
Ruby Question

Ruby Sort an array of arrays of numbers based on multiple conditions

my array looks like this:

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]]


I would like to sort these arrays based on their second and third values in ascending order, but the arrays possessing a negative number for the third number must be sorted decreasingly and placed after the other arrays.

The result should be something like this:

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]]


How can it be done to be as optimized as possible ?

Answer

My understanding is that when a[2] >= 0, sorting is to on the array [a[1], a[2]], and elements for which a[2] < 0 are to be at the end of the sorted array and sorted by [-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 and Enumerable#sort_by rely on the method Array#<=> for determining the ordering of each pair of arrays being sorted. Two arrays, a and b are ordered lexicographically, meaning the following. If a[0] < b[0] then a is less than b (a < b), or equivalently, a <=> b #=> -1. Similarly, if a[0] > b[0] then a is greater than b (a > b) and a <=> b #=> 1. If a[0] == b[0], the tie is broken by the comparing the second elements in the same way, and so on. If a is smaller than b (a.size < b.size), and the first a.size elements of each array are equal, a < b. a and b are equal if and only if a <=> b #=> 0.

Since elements a for which a[2] < 0 are to be placed at the end of the sorted array, we need to sort by arrays whose first elements place the array at the front or back of the sorted array. It is for that reason that I made the first element of the sort-by array zero when a[2] >= 0 and biggest_plus_1 when a[2] < 0, where biggest_plus_1 is the largest value of a[2] plus 1.

The remaining elements of the sort-by arrays determine how each of the two groups of arrays are to be sorted.

Note that biggest_plus_1 will be non-positive if all a[2] < 0, but that doesn't matter, as no element will be sorted by an array whose first element is zero.