tags:

views:

1154

answers:

3

Hi all! I have a Hash like this

{ 55 => {:value=>61, :rating=>-147},
  89 => {:value=>72, :rating=>-175},
  78 => {:value=>64, :rating=>-155},
  84 => {:value=>90, :rating=>-220},
  95 => {:value=>39, :rating=>-92},
  46 => {:value=>97, :rating=>-237},
  52 => {:value=>73, :rating=>-177},
  64 => {:value=>69, :rating=>-167},
  86 => {:value=>68, :rating=>-165},
  53 => {:value=>20, :rating=>-45}
}

How can i sort it by :rating? Or maybe i should use some different structure?

+4  A: 

Hashes in Ruby can't be sorted (at least not before 1.9)

This means that looping through a Hash won't necessarily yield the information in the right order for you. However, it's trivial to loop through Hashed data in a particular order by converting it to an Array first, and in fact calling the sort methods on a Hash will convert it into an Array for you:

>> { :a => 4, :b => 12, :c => 3, :d => 8 }.sort_by { |key, value| value }
=> [[:c, 3], [:a, 4], [:d, 8], [:b, 12]]

So in your case:

hsh.sort_by {|key, ratings| ratings[:rating] }
Gareth
@Gaius - thanks for the edit, but the code works as written. My whole point was that #sort_by implicitly does the Array conversion without you having to stick the .to_a in -- even in Ruby 1.8
Gareth
+1  A: 

There might be a better data structure, but (I'm assuming this is ruby) it's possible to do in Ruby by using the inline sort style to basically tell it how to compare the two. Here's a concrete example:

my_hash = { 
  55 => {:value=>61, :rating=>-147},
  89 => {:value=>72, :rating=>-175},
  78 => {:value=>64, :rating=>-155},
  84 => {:value=>90, :rating=>-220},
  95 => {:value=>39, :rating=>-92},
  46 => {:value=>97, :rating=>-237},
  52 => {:value=>73, :rating=>-177},
  64 => {:value=>69, :rating=>-167},
  86 => {:value=>68, :rating=>-165},
  53 => {:value=>20, :rating=>-45}
}

puts "MY HASH"
my_hash.each do |local|
  puts local
end

sorted_hash = my_hash.sort  { | leftval, rightval | rightval[1][:rating]<=>leftval[1][:rating] }

puts "SORTED HASH"
sorted_hash.each do |local|
  puts local
end
danieltalsky
It won't make much difference here, but #sort is generally inefficient with calculated fields. This is because it will recalculate the sort criteria for *every* comparison. #sort_by calculates the criteria only once for each item in the original array
Gareth
+3  A: 

I would change the data structure to an array of hashes:

my_array =
[
  {:id => 78, :value=>64, :rating=>-155},
  {:id => 84, :value=>90, :rating=>-220},
  {:id => 95, :value=>39, :rating=>-92}
]

You can sort this kind of structure easily with

my_array.sort_by { |record| record[:rating] }

To get the hash-like functionality of fetching a record by id you can define a new method on my_array:

def my_array.find_by_id(id) 
  self.find { |hash| hash[:id] == id }
end

so after that you can do

my_array.find_by_id(id)

instead of

my_hash[id]
Milan Novota