Reputation: 5627
In Ruby, is there a short and sweet way to sort this hash of arrays by score descending:
scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]}
so it looks like this?:
scored = {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
I couldnt find an example where I can sort arrays within a hash like this? I could do this with some nasty code but I feel like there should be a 1 or 2 liner that does it?
Upvotes: 0
Views: 100
Reputation: 110755
order = scored[:score].each_with_index.sort_by(&:first).map(&:last).reverse
#=> [2,0,1]
scored.update(scored) { |_,a| a.values_at *order }
#=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
If scored
is to not to be mutated, replace update
with merge
.
Some points:
order
makes it easy for the reader to understand what's going on.<=>
).arr
in descending order is to use Enumerable#max_by: arr.max_by(arr.size).to_a
.The first line could be replaced with:
arr = scored[:score]
order = arr.each_index.sort_by { |i| arr[i] }.reverse
#=> [2,0,1]
Upvotes: 2
Reputation: 83680
You could use sort_by
scored = {:id=>[1, 2, 3], :score=>[8.3, 5, 10]}
scored.tap do |s|
s[:id] = s[:id].sort_by.with_index{ |a, i| -s[:score][i] }
s[:score] = s[:score].sort_by{ |a| -a }
end
#=> {:id=>[3, 1, 2], :score=>[10, 8.3, 5]}
Upvotes: 3
Reputation: 8787
Here is one possible solution. It has an intermediate step, where it utilizes a zipped version of the scores
object, but produces the correct output:
s = scored.values.inject(&:zip).sort_by(&:last).reverse
#=> [[3, 10], [1, 8.3], [2, 5]]
result = { id: s.map(&:first), score: s.map(&:last) }
#=> { :id => [3, 1, 2], :score => [10, 8.3, 5] }
Upvotes: 1