Ruby: Sorting 2 arrays using values from one of them

Go To


I'm creating a simple game in ruby, and I have two arrays storing high score: $HS_Points and $HS_Names. I'm saving high score in two files, and I wanted to encode points a bit (the points are converted with .to_s(23)). I wanted to sort names and scores in descending order and limit them to 10. So, I need to:

  • Check whether amount of points player scored is higher than amounts of points in high score (on index 0 to 9).
  • If the score received is enough to set player on top 10, put the points and name on that place, pushing other down by one place.

Simple points_array.sort won't do, because it will just sort the points, leaving the names in order they were.

Any ideas how to solve that problem? I'd please to keep answer as simple as possible.

2012-04-03 23:17
by ekharrtoll


Don't store the names and the scores in separate variables; they're tied together, so their data should be tied togeher.

Try, say, $HighScores, which is an array of hashes with a :name element and a :value element, like:

$HighScores = [  
   { :name => "Bob", :score => 234 },
   { :name => "Mark", :score => 2 },   

Then you can add a high score:

$HighScores << { :name => "New Guy", :score => 50000 }

and then re-sort them and take the top 10 in one statement:

$HighScores = $HighScores.sort { |a,b| b[:score] <=> a[:score] }[0,10] 

This will still work if the scores are base-23 encoded. (but why are you doing that?)

You should probably also save them to a single file. To make that easier, consider using the Marshal module.

2012-04-04 11:03
by Michael Slade
Works fine, just need one more thing: how to display them? Before I used "#{$HS_Names[7]}: #{$HS_Points[7]}", but now it obviously won't work - ekharrtoll 2012-04-04 14:22
That would translate to: "#{$HighScores[7][:name]}: #{$HighScores[7][:score]} - Michael Slade 2012-04-04 14:25
gives me an error in '[]': can't convert Symbol into Integer <TypeError>ekharrtoll 2012-04-04 14:34
Perhaps the variable is not the exact format I defined above? I can't tell from here, but I can't see an error in my snippet - Michael Slade 2012-04-04 14:38


Something like this?

names = ['Alice', 'Bob', 'Carol']
points = [22, 11, 33]


You may want the Array#zip method:

pairs =
#=> [["Alice", 22], ["Bob", 11], ["Carol", 33]]


To sort by an array field, compare two fields of the pair.

Sort by name:

pairs.sort{|x,y| x[0] <=> y[0] }  
#=> [["Alice", 22], ["Bob", 11], ["Carol", 33]]

Sort by score:

pairs.sort{|x,y| x[1] <=> y[1] }  
#=> [["Bob", 11], ["Alice", 22], ["Carol", 33]]

Sort By

Another way to sort is the #sort_by method instead of a comparison block (thanks to Niklas B.)

Sort by name:

#=> [["Alice", 22], ["Bob", 11], ["Carol", 33]]

Sort by score:

#=> [["Bob", 11], ["Alice", 22], ["Carol", 33]]


To select just the players above a high score:{|x| x[1] >20 }
#=> [["Alice", 22], ["Carol", 33]]


To unzip:
#=> ["Alice", "Bob", "Carol"]
#=> [22, 11, 33]

These ideas may point you in the right direction.

2012-04-04 00:06
by joelparkerhenderson
The sort snippet can also be written as pairs.sort_by(&:last)Niklas B. 2012-04-04 00:31
@joelparkerhenderson All but sorting worked. I tried to put value, and when I printed out the array AFTER sort: set.sort{|x,y| x[1] <=> y[1] } p set' And I received this: [["One", 100000], ["Two", 80000], ["Three", 50000], ["Four", 30000], ["Testing", 245132]]` "Testing" and 245132 was the value I sent in to test it.

Also, how can I split the zipped aray back into two (one with names and second one with points) - ekharrtoll 2012-04-04 00:42

When you do "sort" it sorts the output; it doesn't change the input array. So when you write "p set" you're really saying "print the original array". One solution is to use the "sort!" method which does change the original array; another solution is to store the results in a different variable e.g. high_scorers = .. - joelparkerhenderson 2012-04-04 01:55