Reputation: 1076
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.
Upvotes: 3
Views: 507
Reputation: 9225
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']] }
Upvotes: 0
Reputation: 29328
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"}]
Upvotes: 0
Reputation: 37409
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
Upvotes: 2