Question

I have a hash that contains another hash inside and I need to sort as follow:

data = {
    "user": {
        "name": "John",
        "age": "36",
    },
    "company": "Terminal",
    "country": "Canada",
} 
{
    "user": {
        "name": "Mary",
        "age": "32",
    },
    "company": "Brasco",
    "country": "Italy",
}

Using this way I can sort without problem:

data.sort_by { |a| [a[:company], a[:country]] }

But in case "company" and "country" have the same value, I need to sort by a third option, the "name".

The case is that I need to use the function 'reverse' at the end (the example I posted is not the real situation, the real one is too big...lol).

So the result is sorting the name reversed too.

data.sort_by { |a| [a['company'], a['country'], a['user']['name']] }.reverse

I need the "company" and "country" sort in DESC and user.name sort by ASC.

Was it helpful?

Solution

data.sort_by { |a| [a['company'], a['country'], a['user']['name']] }

When sort_by receives an array, it compares each element when the previous ones tie.

If you want to sort company ASC, country ASC, name ASC and then easily reverse it to company DESC, country DESC, name ASC you can do this:

sorted = data.group_by do |x| 
  [x['company'], x['country']] 
end.each do |k, v| 
  v.sort_by! { |x| x[:user][:name] } 
end.sort_by(&:first).map(&:last)

This creates an array of arrays, each internal array contains all the users in the company/country pair, sorted by name.

now to get the ASC option you should do:

sorted.flatten

and the reverse (where users are still ASC) you need to:

sorted.reverse.flatten

OTHER TIPS

How about this

data = [
{
 "user" => {
    "name" => 'John',
    "age" => "36",
  },
  "company" => "Terminal"
  "country" => "Canada"
},
{
"user" => {
    "name" => 'Mary',
    "age" => "32",
  },
  "company" => "Brasco",
  "country" => "Italy"
},
{
"user" => {
    "name" => 'Andre',
    "age" => "32",
  },
  "company" => "Brasco",
  "country" => "Italy"
}]


data.sort do |a,b| 
  sort_direction = [a['company'],a['country']] <=> [b['company'],b['country']]
  b['user']['name'] <=> a['user']['name'] if sort_direction == 0
end
#=>=> [{"user"=>{"name"=>"Mary", "age"=>"32"}, "company"=>"Brasco", "country"=>"Italy"}{"user"=>{"name"=>"Andre", "age"=>"32"},"company"=>"Brasco", "country"=>"Italy"},{"user"=>{"name"=>"John", "age"=>"36"}, "company"=>"Terminal","country"=>"Canada"}]

Possible way if you do it often:

class Reverser
  attr_reader :v

  def initialize(v)
    @v = v
  end

  def self.[](v)
    new(v)
  end

  def <=>(other)
    other.v <=> @v
  end
end

# Order by company DESC, country DESC, user.name ASC
data.sort_by { |a| [Reverser[a['company']], Reverser[a['country']], a['user']['name']] }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top