Matthieu Raynaud de Fitte - 1 year ago 133
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 ?

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.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download