Can someone show me how to sort a nested array based on custom strings? For example, is there a way to sort:
[['Red','Blue'],['Green','Orange'],['Purple','Yellow']]
by "Orange"
, "Yellow"
, and then "Blue"
? The end result would look like this:
[['Green','Orange'],['Purple','Yellow'],['Red','Blue']]
It's not sorting by alphabetical order. I'm curious to know if I can define the values to sort by to accomplish the goal above.
To define custom sort function, you need to compare first value with second value. If first value is greater than the second value, return -1. If first value is less than the second value, return 1 otherwise return 0. The above process will sort the data in descending order.
The Ruby sort method works by comparing elements of a collection using their <=> operator (more about that in a second), using the quicksort algorithm. You can also pass it an optional block if you want to do some custom sorting. The block receives two parameters for you to specify how they should be compared.
sort_by
is always very handy for that kind of sorting:
a = [['Red','Blue'],['Green','Orange'],['Purple','Yellow']]
order_array = ['Orange', 'Yellow', 'Blue']
p a.sort_by { |arr| order_array.index(arr[1]) }
# => [["Green", "Orange"], ["Purple", "Yellow"], ["Red", "Blue"]]
This is a task for group_by
and values_at
:
ORDER = %w[Orange Yellow Blue]
ary = [['Red','Blue'],['Green','Orange'],['Purple','Yellow']]
ary.group_by{ |a| a.last }.values_at(*ORDER)
# => [[["Green", "Orange"]], [["Purple", "Yellow"]], [["Red", "Blue"]]]
Here's what group_by
brings to the party:
ary.group_by{ |a| a.last }
# => {"Blue"=>[["Red", "Blue"]],
# "Orange"=>[["Green", "Orange"]],
# "Yellow"=>[["Purple", "Yellow"]]}
Once you have the hash of values used to group each array, then values_at
makes it easy to extract them in the right order.
This is an extremely fast and efficient way to do the task as it will barely slow down as ary
grows due to the fact that there is no real sorting going on, it's just grouping by a value, then extracting from the hash in a given order.
If you want the exact same array-of-arrays as in your example, flatten
the result once:
ary.group_by{ |a| a.last }.values_at(*ORDER).flatten(1)
# => [["Green", "Orange"], ["Purple", "Yellow"], ["Red", "Blue"]]
You won't want to do that if there are going to be multiple "Orange", "Yellow" or "Blue" elements as the result won't be very usable.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With