I'm being sent a nested hash that needs to be sorted by its values. For example:
@foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}}
When running the following:
@foo["a"].sort{|a,b| a[1]<=>b[1]}
I get:
[["y", 3], ["z", 5], ["x", 88]]
This is great, it's exactly what I want. The problem is I'm not always going to know what all the keys are that are being sent to me so I need some sort of loop. I tried to do the following:
@foo.each do |e|
e.sort{|a,b| a[1]<=>b[1]}
end
This to me makes sense since if I manually call @foo.first[0] I get
"a"
and @foo.first[1] returns
{"z"=>5, "y"=>3, "x"=>8}
but for some reason this isn't sorting properly (e.g. at all). I assume this is because the each is calling sort on the entire hash object rather than on "a"'s values. How do I access the values of the nested hash without knowing what it's key is?
You might want to loop over the hash like this:
@foo.each do |key, value| @foo[key] = value.sort{ |a,b| a[1]<=>b[1] } end
@foo = {"a"=>{"z"=>5, "y"=>3, "x"=>88}, "b"=>{"a"=>2, "d"=>-5}}
@bar = Hash[ @foo.map{ |key,values| [ key, values.sort_by(&:last) ] } ]
Or, via a less-tricky path:
@bar = {}
@foo.each do |key,values|
@bar[key] = values.sort_by{ |key,value| value }
end
In both cases @bar
turns out to be:
p @bar
#=> {
#=> "a"=>[["y", 3], ["z", 5], ["x", 88]],
#=> "b"=>[["d", -5], ["a", 2]]
#=> }
My coworker came up with a slightly more flexible solution that will recursively sort an array of any depth:
def deep_sort_by(&block)
Hash[self.map do |key, value|
[if key.respond_to? :deep_sort_by
key.deep_sort_by(&block)
else
key
end,
if value.respond_to? :deep_sort_by
value.deep_sort_by(&block)
else
value
end]
end.sort_by(&block)]
end
You can inject it into all hashes and then just call it like this:
myMap.deep_sort_by { |obj| obj }
The code would be similar for an array. We published it as a gem for others to use, see blog post for additional details.
Disclaimer: I work for this company.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With