[英]How to sort hash in ruby recursively
I have a huge hash/json in below format.我有以下格式的巨大哈希/json。 Sample is given.
给出了样品。 How we can sort this result hash based on the "total" key in descending order?
我们如何根据“total”键以降序对这个结果哈希进行排序?
Nb: The last level nested node will not have have response
and total
keys.注意:最后一级嵌套节点将没有
response
和total
键。 They will have metrics
as keys.他们将
metrics
作为键。
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 } },
}
}
}
}
}
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 )类似于:(工作示例: 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}}}}}}}
Let's first simplify your example.让我们首先简化您的示例。
result = {
"account_id_1": {
"total": 1000,
"response": {
"location_1": { "total": 300, "response": 1 },
"location_2": { "total": 500, "response": 2 },
"location_3": { "total": 200, "response": 3 }
}
},
"account_id_2": {
"total": 2000,
"response": {
"location_1": { "total": 300, "response": 1 },
"location_2": { "total": 500, "response": 2 },
"location_3": { "total": 1200, "response": 3 }
}
}
}
In your example,在你的例子中,
result[:account_id_1][:response][:location_1][:response]
#=> { "service_1": { "metrics": { "cost": 100} },
# "service_2": { "metrics": { "cost": 100 } },
# "service_3": { "metrics": { "cost": 100 } } }
This value, and others like it, will remain unchanged, so I've replaced it with a placeholder, an arbitrary integer:这个值和其他类似的值将保持不变,所以我用占位符替换了它,一个任意整数:
result[:account_id_1][:response][:location_1][:response]
#=> 1
result[:account_id_1][:response][:location_2][:response]
#=> 2
and so on.等等。
I found it useful to create a helper method:我发现创建一个辅助方法很有用:
def sort_by_total_desc(h)
h.sort_by { |_,g| -g[:total] }.to_h
end
The desired hash can then be computed as follows.然后可以如下计算所需的哈希值。
sort_by_total_desc(result.transform_values do |v|
v.merge(response: sort_by_total_desc(v[:response]))
end)
#=> {:account_id_2=>{
# :total=>2000,
# :response=>{
# :location_3=>{:total=>1200, :response=>3},
# :location_2=>{:total=>500, :response=>2},
# :location_1=>{:total=>300, :response=>1}
# }
# },
# :account_id_1=>{
# :total=>1000,
# :response=>{
# :location_2=>{:total=>500, :response=>2},
# :location_1=>{:total=>300, :response=>1},
# :location_3=>{:total=>200, :response=>3}
# }
# }
# }
Regarding the helper method, here is an example.关于辅助方法,这里有一个例子。
h = { "location_1": { "total": 300, "response": 1 },
"location_2": { "total": 500, "response": 2 },
"location_3": { "total": 200, "response": 3 } }
sort_by_total_desc(h)
#=> {:location_2=>{:total=>500, :response=>2},
# :location_1=>{:total=>300, :response=>1},
# :location_3=>{:total=>200, :response=>3}}
which requires two steps:这需要两个步骤:
a = h.sort_by { |_,g| -g[:total] }
#=> [[:location_2, {:total=>500, :response=>2}],
# [:location_1, {:total=>300, :response=>1}],
# [:location_3, {:total=>200, :response=>3}]]
a.to_h
#=> as above
The main method has two main steps.主要方法有两个主要步骤。 First perform the "inner" sorting.
首先执行“内部”排序。
h = result.transform_values do |v|
v.merge(response: sort_by_total_desc(v[:response]))
end
#=> {:account_id_1=>{
# :total=>1000,
# :response=>{
# :location_2=>{:total=>500, :response=>2},
# :location_1=>{:total=>300, :response=>1},
# :location_3=>{:total=>200, :response=>3}
# }
# },
# :account_id_2=>{
# :total=>2000,
# :response=>{
# :location_3=>{:total=>1200, :response=>3},
# :location_2=>{:total=>500, :response=>2},
# :location_1=>{:total=>300, :response=>1}
# }
# }
# }
Then do the "outer" sorting to return the desired result:然后进行“外部”排序以返回所需的结果:
sort_by_total_desc(h)
#=> as above
See Enumerable#sort_by , Hash#transform_values and Hash#merge .请参阅Enumerable#sort_by 、 Hash#transform_values和Hash#merge 。
您可以使用以下代码根据总值进行排序。
result.sort_by { |_key,value| value[:total] }.reverse.to_h
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.