3

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?

0

4 Answers 4

4

This is a three step approach, though I'm sure there is another more terse answer.

First we sort the values where the 3rd element is positive and/or zero:

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

We then sort the values where the 3rd element is negative:

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

We then combine those together:

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]]
Sign up to request clarification or add additional context in comments.

1 Comment

sort_by { |arr| [arr[2], arr[3]] can you explain how it works, you should have first a sorting following arr[2] and subsequently for an equal arr[2], a sorting following the arr[3] .....+1
1

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.

1 Comment

thanks a lots, it works very well ( a[1] > 0, sorry about the mistake ).Could you please explain how it works or give me a link explaining how the syntax works please ?
1

My variant for your question is:

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

also, we can use just .abs and it will looks like:

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

but in this way -2 == 2 return true and result will be like it:

#=> [[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]]

Comments

1

The sort by creates and array and uses that to sort the values. All we need do do is to create an array that follow the logic needed. In order:

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

Result:

#=> [[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]]

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.