Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically sorting an array of hashes in Ruby

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!

like image 422
Steve Avatar asked Apr 16 '26 02:04

Steve


1 Answers

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.

like image 98
Igor Drozdov Avatar answered Apr 17 '26 15:04

Igor Drozdov



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!