I would like to sort an array of hashes by several dynamic criteria. Let's say I have this array
persons = [
{
id: 1,
first_name: "Bill",
last_name: "Zamora",
age: 37
},
{
id: 2,
first_name: "Alexia",
last_name: "Reyes",
age: 70
},
{
id: 3,
first_name: "Anthony",
last_name: "Nelson",
age: 25
}
]
I know that you can easily sort an array by multiple criteria with the following code
persons.sort_by!{ |p| [p[:age], p[:first_name]] }
However, in this example the number and the order of the fields by which the array is sorted is hard-coded. In my case, this is determined dynamically at runtime. So I do not know by how many fields the array is to be sorted nor what fields are sorted in which order.
I am looking for an elegant solution to sort my array using a configuration object that I don't know before. Such a configuration could look like this:
sort_settings = [
{
field: "first_name",
order: "asc"
},
{
field: "age",
order: "desc"
}
]
I am very grateful for any help on this!
It's quite challenging to sort strings in desc order using sort_by, it'd be better to use "lower-level" sort method, which sorts by specified comparator using <=> operator. A quick solution for this looks something like:
persons.sort do |a, b|
comparator = 0
sort_settings.each do |s|
a_field = a[s[:field].to_sym]
b_field = b[s[:field].to_sym]
comparator = a_field <=> b_field
comparator = -comparator if s[:order] == "desc"
break unless comparator == 0
end
comparator
end
The block must implement a comparison between a and b, and return -1, when a follows b, 0 when a and b are equivalent, or +1 if b follows a.
Thus, we iterate over sort_settings and compare specified fields using <=>, which returns 1, 0 or -1. If the specified order is desc, we invert the value. If comparator returns something different to zero, we don't need to continue iterations.
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