Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sort array using custom sorting preferences?

Tags:

ruby

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.

like image 670
LewlSauce Avatar asked Oct 11 '14 23:10

LewlSauce


People also ask

How do you sort a custom array?

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.

How do you sort a list in Ruby?

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.


2 Answers

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"]]
like image 147
Patrice Gahide Avatar answered Nov 06 '22 01:11

Patrice Gahide


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.

like image 33
the Tin Man Avatar answered Nov 06 '22 02:11

the Tin Man