I have a huge hash/json in below format. Sample is given. How we can sort this result hash based on the "total" key in descending order?
Nb: The last level nested node will not have have response
and total
keys. They will have metrics
as keys.
result = {
"account_id_1": {
"total": 1000,
"response": {
"location_1": {
"total": 300,
"response": {
"service_1": { "metrics": { "cost": 100} },
"service_2": { "metrics": { "cost": 100 } },
"service_3": { "metrics": { "cost": 100 } },
}
},
"location_2": {
"total": 500,
"response": {
"service_1": { "metrics": { "cost": 300 } },
"service_2": { "metrics": { "cost": 150 } },
"service_3": { "metrics": { "cost": 50 } },
}
},
"location_3": {
"total": 200,
"response": {
"service_1": { "metrics": { "cost": 75 } },
"service_2": { "metrics": { "cost": 75 } },
"service_3": { "metrics": { "cost": 50 } },
}
}
}
},
"account_id_2": {
"total": 2000,
"response": {
"location_1": {
"total": 300,
"response": {
"service_1": { "metrics": { "cost": 100 } },
"service_2": { "metrics": { "cost": 100 } },
"service_3": { "metrics": { "cost": 100 } },
}
},
"location_2": {
"total": 500,
"response": {
"service_1": { "metrics": { "cost": 300 } },
"service_2": { "metrics": { "cost": 150 } },
"service_3": { "metrics": { "cost": 50 } },
}
},
"location_3": {
"total": 1200,
"response": {
"service_1": { "metrics": { "cost": 1075 } },
"service_2": { "metrics": { "cost": 75 } },
"service_3": { "metrics": { "cost": 50 } },
}
}
}
}
}
Expected result:
result = {
"account_id_2": {
"total": 2000,
"response": {
"location_3": {
"total": 1200,
"response": {
"service_1": { "metrics": { "cost": 1075 } },
"service_2": { "metrics": { "cost": 75 } },
"service_3": { "metrics": { "cost": 50 } },
}
},
"location_2": {
"total": 500,
"response": {
"service_1": { "metrics": { "cost": 300 } },
"service_2": { "metrics": { "cost": 150 } },
"service_3": { "metrics": { "cost": 50 } },
}
},
"location_1": {
"total": 300,
"response": {
"service_1": { "metrics": { "cost": 100 } },
"service_2": { "metrics": { "cost": 100 } },
"service_3": { "metrics": { "cost": 100 } },
}
}
}
},
"account_id_1": {
"total": 1000,
"response": {
"location_2": {
"total": 500,
"response": {
"service_1": { "metrics": { "cost": 300 } },
"service_2": { "metrics": { "cost": 150 } },
"service_3": { "metrics": { "cost": 50 } },
}
},
"location_1": {
"total": 300,
"response": {
"service_1": { "metrics": { "cost": 100} },
"service_2": { "metrics": { "cost": 100 } },
"service_3": { "metrics": { "cost": 100 } },
}
},
"location_3": {
"total": 200,
"response": {
"service_1": { "metrics": { "cost": 75 } },
"service_2": { "metrics": { "cost": 75 } },
"service_3": { "metrics": { "cost": 50 } },
}
}
}
}
}
CodePudding user response:
You can use below code for sorting based on total value.
result.sort_by { |_key,value| value[:total] }.reverse.to_h
CodePudding user response:
Personally I would create a class to handle the custom sorting so that we can implement our own <=>
(Spaceship Operator)
Something like: (Working Example: https://replit.com/@engineersmnky/ThankfulAutomaticRam#main.rb)
class AccountLocaleMetricSorter
include Comparable
def self.sort(h)
h.map do |name,values|
new(name: name, data: values)
end.sort.reduce({}) {|m,o| m.merge(o.to_h)}
end
attr_reader :name, :data, :o_data
def initialize(name: , data: )
@name = name
@o_data = data
@data = parse_response(data)
end
def to_h
return {name.to_sym => o_data} unless o_data.key?(:total)
{name.to_sym => {
total: o_data[:total],
response: data.to_h
}
}
end
def <=>(other)
if o_data.key?(:total) && o_data.key?(:total)
other.o_data[:total] <=> o_data[:total]
elsif other.o_data.key?(:metrics) && o_data.key?(:metrics)
other.o_data.dig(:metrics,:cost) <=> o_data.dig(:metrics,:cost)
else
0
end
end
private
def parse_response(response)
return response unless response.key?(:response)
self.class.sort(response[:response])
end
end
Usage as:
pp AccountLocaleMetricSorter.sort(result)
Output:
{:account_id_2=>
{:total=>2000,
:response=>
{:location_3=>
{:total=>1200,
:response=>
{:service_1=>{:metrics=>{:cost=>1075}},
:service_2=>{:metrics=>{:cost=>75}},
:service_3=>{:metrics=>{:cost=>50}}}},
:location_2=>
{:total=>500,
:response=>
{:service_1=>{:metrics=>{:cost=>300}},
:service_2=>{:metrics=>{:cost=>150}},
:service_3=>{:metrics=>{:cost=>50}}}},
:location_1=>
{:total=>300,
:response=>
{:service_1=>{:metrics=>{:cost=>100}},
:service_2=>{:metrics=>{:cost=>100}},
:service_3=>{:metrics=>{:cost=>100}}}}}},
:account_id_1=>
{:total=>1000,
:response=>
{:location_2=>
{:total=>500,
:response=>
{:service_3=>{:metrics=>{:cost=>300}},
:service_2=>{:metrics=>{:cost=>150}},
:service_1=>{:metrics=>{:cost=>50}}}},
:location_1=>
{:total=>300,
:response=>
{:service_1=>{:metrics=>{:cost=>100}},
:service_2=>{:metrics=>{:cost=>100}},
:service_3=>{:metrics=>{:cost=>100}}}},
:location_3=>
{:total=>200,
:response=>
{:service_1=>{:metrics=>{:cost=>75}},
:service_2=>{:metrics=>{:cost=>75}},
:service_3=>{:metrics=>{:cost=>50}}}}}}}