Velocity Reviews - Computer Hardware Reviews

Velocity Reviews > Newsgroups > Programming > Ruby > Consecutive sort on Array of Hashes

Reply
Thread Tools

Consecutive sort on Array of Hashes

 
 
Laurent Colloud
Guest
Posts: n/a
 
      08-14-2006
Hi,

Here my - strange - problem.

To explain it, let's take the example of football. I construct an array
of hashes of the results with team_id, total of pts, number of wins,
number of draws and number of defeats such as:

myArray = Array.new
myArray << {:team_id=>1, ts=>5, :w=>0, :d=>5, :l=>5}
myArray << {:team_id=>2, ts=>7, :w=>1, :d=>4, :l=>5}
myArray << {:team_id=>3, ts=>4, :w=>0, :d=>4, :l=>6}
myArray << {:team_id=>4, ts=>6, :w=>1, :d=>3, :l=>6}
myArray << {:team_id=>5, ts=>5, :w=>0, :d=>5, :l=>5}
myArray << {:team_id=>6, ts=>5, :w=>0, :d=>5, :l=>5}
myArray << {:team_id=>8, ts=>10, :w=>2, :d=>4, :l=>4}
myArray << {:team_id=>9, ts=>5, :w=>1, :d=>2, :l=>7}
myArray << {:team_id=>10, ts=>8, :w=>1, :d=>5, :l=>4}
myArray << {:team_id=>11, ts=>9, :w=>2, :d=>3, :l=>5}
myArray << {:team_id=>12, ts=>6, :w=>1, :d=>3, :l=>6}
myArray << {:team_id=>13, ts=>5, :w=>0, :d=>5, :l=>5}

Now, from this array, I want to get the table.
So what I want to do is to sort the array, first by total of pts, then
by number of wins (if 2 teams have the same total of points I put first
the team with more wins) and then by number of draws.

That's how - logically - I would do it anyway. But that does not seem to
be the Ruby way. Doing it that way gives me a complete mess.

No worries, doing it the other way round works (first sort by draw, then
wins, then pts)! Here is what I run:

puts "Team, Pts, W, D, L"
myArray = myArray.sort { |a,b| b[:d] <=> a[:d]
}.sort { |a,b| b[:w] <=> a[:w]
}.sort { |a,b| b[ts] <=> a[ts]
}.each do |row|
puts "#{row[:team_id]}, #{row[ts]}, #{row[:w]}, #{row[:d]},
#{row[:l]}"
end

Did I say it works? Well almost! Here is my output:

Team, Pts, W, D, L
8, 10, 2, 4, 4
11, 9, 2, 3, 5
10, 8, 1, 5, 4
2, 7, 1, 4, 5
4, 6, 1, 3, 6
12, 6, 1, 3, 6
16, 5, 0, 5, 5
1, 5, 0, 5, 5
9, 5, 1, 2, 7
5, 5, 0, 5, 5
6, 5, 0, 5, 5
3, 4, 0, 4, 6

Good news, we kept the correct team_is with the correct result.
But can anyone explain me what on earth is the line 9, 5, 1, 2, 7 doing
there in the middle?

Thanks for your help!

--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
 
James Edward Gray II
Guest
Posts: n/a
 
      08-14-2006
On Aug 14, 2006, at 4:04 PM, Laurent Colloud wrote:

> Hi,
>
> Here my - strange - problem.
>
> To explain it, let's take the example of football. I construct an
> array
> of hashes of the results with team_id, total of pts, number of wins,
> number of draws and number of defeats such as:
>
> myArray = Array.new
> myArray << {:team_id=>1, ts=>5, :w=>0, :d=>5, :l=>5}
> myArray << {:team_id=>2, ts=>7, :w=>1, :d=>4, :l=>5}
> myArray << {:team_id=>3, ts=>4, :w=>0, :d=>4, :l=>6}
> myArray << {:team_id=>4, ts=>6, :w=>1, :d=>3, :l=>6}
> myArray << {:team_id=>5, ts=>5, :w=>0, :d=>5, :l=>5}
> myArray << {:team_id=>6, ts=>5, :w=>0, :d=>5, :l=>5}
> myArray << {:team_id=>8, ts=>10, :w=>2, :d=>4, :l=>4}
> myArray << {:team_id=>9, ts=>5, :w=>1, :d=>2, :l=>7}
> myArray << {:team_id=>10, ts=>8, :w=>1, :d=>5, :l=>4}
> myArray << {:team_id=>11, ts=>9, :w=>2, :d=>3, :l=>5}
> myArray << {:team_id=>12, ts=>6, :w=>1, :d=>3, :l=>6}
> myArray << {:team_id=>13, ts=>5, :w=>0, :d=>5, :l=>5}
>
> Now, from this array, I want to get the table.
> So what I want to do is to sort the array, first by total of pts,
> then
> by number of wins (if 2 teams have the same total of points I put
> first
> the team with more wins) and then by number of draws.


Does this do what you were after?

$ irb -r pp
>> pp myArray.sort_by { |team| [team[ts], team[:w], team

[:d]] }.reverse
[{:l=>4, :team_id=>8, ts=>10, :w=>2, :d=>4},
{:l=>5, :team_id=>11, ts=>9, :w=>2, :d=>3},
{:l=>4, :team_id=>10, ts=>8, :w=>1, :d=>5},
{:l=>5, :team_id=>2, ts=>7, :w=>1, :d=>4},
{:l=>6, :team_id=>12, ts=>6, :w=>1, :d=>3},
{:l=>6, :team_id=>4, ts=>6, :w=>1, :d=>3},
{:l=>7, :team_id=>9, ts=>5, :w=>1, :d=>2},
{:l=>5, :team_id=>13, ts=>5, :w=>0, :d=>5},
{:l=>5, :team_id=>6, ts=>5, :w=>0, :d=>5},
{:l=>5, :team_id=>5, ts=>5, :w=>0, :d=>5},
{:l=>5, :team_id=>1, ts=>5, :w=>0, :d=>5},
{:l=>6, :team_id=>3, ts=>4, :w=>0, :d=>4}]
=> nil

Hope that helps.

James Edward Gray II


 
Reply With Quote
 
 
 
 
Gennady Bystritsky
Guest
Posts: n/a
 
      08-14-2006
> Hi,
>=20
> Here my - strange - problem.
>=20
> To explain it, let's take the example of football. I=20
> construct an array=20
> of hashes of the results with team_id, total of pts, number of wins,=20
> number of draws and number of defeats such as:
>=20
> myArray =3D Array.new
> myArray << {:team_id=3D>1, ts=3D>5, :w=3D>0, :d=3D>5, :l=3D>5}
> myArray << {:team_id=3D>2, ts=3D>7, :w=3D>1, :d=3D>4, :l=3D>5}
> myArray << {:team_id=3D>3, ts=3D>4, :w=3D>0, :d=3D>4, :l=3D>6}
> myArray << {:team_id=3D>4, ts=3D>6, :w=3D>1, :d=3D>3, :l=3D>6}
> myArray << {:team_id=3D>5, ts=3D>5, :w=3D>0, :d=3D>5, :l=3D>5}
> myArray << {:team_id=3D>6, ts=3D>5, :w=3D>0, :d=3D>5, :l=3D>5}
> myArray << {:team_id=3D>8, ts=3D>10, :w=3D>2, :d=3D>4, :l=3D>4}
> myArray << {:team_id=3D>9, ts=3D>5, :w=3D>1, :d=3D>2, :l=3D>7}
> myArray << {:team_id=3D>10, ts=3D>8, :w=3D>1, :d=3D>5, :l=3D>4}
> myArray << {:team_id=3D>11, ts=3D>9, :w=3D>2, :d=3D>3, :l=3D>5}
> myArray << {:team_id=3D>12, ts=3D>6, :w=3D>1, :d=3D>3, :l=3D>6}
> myArray << {:team_id=3D>13, ts=3D>5, :w=3D>0, :d=3D>5, :l=3D>5}
>=20
> Now, from this array, I want to get the table.
> So what I want to do is to sort the array, first by total of=20
> pts, then=20
> by number of wins (if 2 teams have the same total of points I=20
> put first=20
> the team with more wins) and then by number of draws.
>=20
> That's how - logically - I would do it anyway. But that does=20
> not seem to=20
> be the Ruby way. Doing it that way gives me a complete mess.
>=20
> No worries, doing it the other way round works (first sort by=20
> draw, then=20
> wins, then pts)! Here is what I run:
>=20
> puts "Team, Pts, W, D, L"
> myArray =3D myArray.sort { |a,b| b[:d] <=3D> a[:d]
> }.sort { |a,b| b[:w] <=3D> a[:w]
> }.sort { |a,b| b[ts] <=3D> a[ts]
> }.each do |row|
> puts "#{row[:team_id]}, #{row[ts]}, #{row[:w]}, #{row[:d]},=20
> #{row[:l]}"
> end
>=20
> Did I say it works? Well almost! Here is my output:
>=20
> Team, Pts, W, D, L
> 8, 10, 2, 4, 4
> 11, 9, 2, 3, 5
> 10, 8, 1, 5, 4
> 2, 7, 1, 4, 5
> 4, 6, 1, 3, 6
> 12, 6, 1, 3, 6
> 16, 5, 0, 5, 5
> 1, 5, 0, 5, 5
> 9, 5, 1, 2, 7
> 5, 5, 0, 5, 5
> 6, 5, 0, 5, 5
> 3, 4, 0, 4, 6
>=20
> Good news, we kept the correct team_is with the correct result.
> But can anyone explain me what on earth is the line 9, 5, 1,=20
> 2, 7 doing=20
> there in the middle?
>=20
> Thanks for your help!


Each of your sort's just re-arranges the array by the new criteria,
completely unrelated to previous sorts. Why not simply to create an
appropriate criteria for a single sort:

[ DISCLAIMER: Untested code bellow ]

myArray =3D myArray.sort { |b, a|=20
diff =3D 0
[ ts, :w, :d ].each do |_criteria|
diff =3D a[_criteria] - b[_criteria]
break unless diff.zero?
end
diff
}

Or even cooler

myArray =3D myArray.sort { |a, b|
[ ts, :w, :d ].inject(0) { |_m, _c|
_m =3D=3D 0 ? b[_c] - a[_c] : _m
}
}

Gennady.

>=20
> --=20
> Posted via http://www.ruby-forum.com/.
>=20
>=20


 
Reply With Quote
 
Ken Bloom
Guest
Posts: n/a
 
      08-15-2006
On Tue, 15 Aug 2006 07:02:52 +0900, Gennady Bystritsky wrote:

>> Hi,
>>
>> Here my - strange - problem.
>>
>> To explain it, let's take the example of football. I
>> construct an array
>> of hashes of the results with team_id, total of pts, number of wins,
>> number of draws and number of defeats such as:
>>
>> myArray = Array.new
>> myArray << {:team_id=>1, ts=>5, :w=>0, :d=>5, :l=>5}
>> myArray << {:team_id=>2, ts=>7, :w=>1, :d=>4, :l=>5}
>> myArray << {:team_id=>3, ts=>4, :w=>0, :d=>4, :l=>6}
>> myArray << {:team_id=>4, ts=>6, :w=>1, :d=>3, :l=>6}
>> myArray << {:team_id=>5, ts=>5, :w=>0, :d=>5, :l=>5}
>> myArray << {:team_id=>6, ts=>5, :w=>0, :d=>5, :l=>5}
>> myArray << {:team_id=>8, ts=>10, :w=>2, :d=>4, :l=>4}
>> myArray << {:team_id=>9, ts=>5, :w=>1, :d=>2, :l=>7}
>> myArray << {:team_id=>10, ts=>8, :w=>1, :d=>5, :l=>4}
>> myArray << {:team_id=>11, ts=>9, :w=>2, :d=>3, :l=>5}
>> myArray << {:team_id=>12, ts=>6, :w=>1, :d=>3, :l=>6}
>> myArray << {:team_id=>13, ts=>5, :w=>0, :d=>5, :l=>5}
>>
>> Now, from this array, I want to get the table.
>> So what I want to do is to sort the array, first by total of
>> pts, then
>> by number of wins (if 2 teams have the same total of points I
>> put first
>> the team with more wins) and then by number of draws.
>>
>> That's how - logically - I would do it anyway. But that does
>> not seem to
>> be the Ruby way. Doing it that way gives me a complete mess.
>>
>> No worries, doing it the other way round works (first sort by
>> draw, then
>> wins, then pts)! Here is what I run:
>>
>> puts "Team, Pts, W, D, L"
>> myArray = myArray.sort { |a,b| b[:d] <=> a[:d]
>> }.sort { |a,b| b[:w] <=> a[:w]
>> }.sort { |a,b| b[ts] <=> a[ts]
>> }.each do |row|
>> puts "#{row[:team_id]}, #{row[ts]}, #{row[:w]}, #{row[:d]},
>> #{row[:l]}"
>> end
>>
>> Did I say it works? Well almost! Here is my output:
>>
>> Team, Pts, W, D, L
>> 8, 10, 2, 4, 4
>> 11, 9, 2, 3, 5
>> 10, 8, 1, 5, 4
>> 2, 7, 1, 4, 5
>> 4, 6, 1, 3, 6
>> 12, 6, 1, 3, 6
>> 16, 5, 0, 5, 5
>> 1, 5, 0, 5, 5
>> 9, 5, 1, 2, 7
>> 5, 5, 0, 5, 5
>> 6, 5, 0, 5, 5
>> 3, 4, 0, 4, 6
>>
>> Good news, we kept the correct team_is with the correct result.
>> But can anyone explain me what on earth is the line 9, 5, 1,
>> 2, 7 doing
>> there in the middle?
>>
>> Thanks for your help!

>
> Each of your sort's just re-arranges the array by the new criteria,
> completely unrelated to previous sorts. Why not simply to create an
> appropriate criteria for a single sort:


Not of .sort is a stable sort. Then it will preserve the order of items
that are equal according to the current comparison. You would sort from
least significant to most significant exactly as he has done here.

From the results though, I conclude that .sort isn't a stable sort.

Looking through the C code, I see that .sort is a quicksort.

http://en.wikipedia.org/wiki/Sorting...ing_algorithms

--Ken

--
Ken Bloom. PhD candidate. Linguistic Cognition Laboratory.
Department of Computer Science. Illinois Institute of Technology.
http://www.iit.edu/~kbloom1/
 
Reply With Quote
 
Kroeger, Simon (ext)
Guest
Posts: n/a
 
      08-15-2006
Hi,=20

> From: Laurent Colloud
> Sent: Monday, August 14, 2006 11:05 PM
>=20
> Hi,
>=20
> Here my - strange - problem.
>=20
> To explain it, let's take the example of football. I=20
> construct an array=20
> of hashes of the results with team_id, total of pts, number of wins,=20
> number of draws and number of defeats such as:
> [...]
>=20
> puts "Team, Pts, W, D, L"
> myArray =3D myArray.sort { |a,b| b[:d] <=3D> a[:d]
> }.sort { |a,b| b[:w] <=3D> a[:w]
> }.sort { |a,b| b[ts] <=3D> a[ts]
> }.each do |row|
> puts "#{row[:team_id]}, #{row[ts]}, #{row[:w]}, #{row[:d]},=20
> #{row[:l]}"
> end


try

myArray.sort {|a,b|=20
(b[:d] <=3D> a[:d]).nonzero? ||
(b[:w] <=3D> a[:w]).nonzero? ||
(b[ts] <=3D> a[ts])}

or (even better):

myArray.sort_by {|a| [a[:d], a[:w], a[ts]]}

cheers

Simon

 
Reply With Quote
 
Laurent Colloud
Guest
Posts: n/a
 
      08-15-2006
Hi guys,

First thank you very much for helping me out on that one.

So here are the results of all your suggestions. And the winner is...

-----------------------------------
THEY WORK
-----------------------------------

By James:

myArray.sort_by { |team| [team[ts], team[:w], team[:d]] }.reverse


By Gennady (nice one!):

myArray.sort { |b, a|
diff = 0
[ ts, :w, :d ].each do |_criteria|
diff = a[_criteria] - b[_criteria]
break unless diff.zero?
end
diff
}


By Gennady (nice one again!):

myArray.sort { |a, b|
[ ts, :w, :d ].inject(0) { |_m, _c|
_m == 0 ? b[_c] - a[_c] : _m
}
}

By Russell (with a few synthax corrections):

myArray.sort {|a,b|
if b[ts] == a[ts]
if b[:w] == a[:w]
b[:d] <=> a[:d]
else
b[:w] <=> a[:w]
end
else
b[ts] <=> a[ts]
end
}

-----------------------------------------------
THEY DON'T WORK
-----------------------------------------------

myArray.sort_by {|a| [a[:d], a[:w], a[ts]]}

-> Sorted by draws (:d) ASC (and then :w ASC, ts ASC) but I could
deduct James'solution from the output of this so thanks anyway .

myArray.sort {|a,b|
(b[:d] <=> a[:d]).nonzero? ||
(b[:w] <=> a[:w]).nonzero? ||
(b[ts] <=> a[ts])}

->Sorted by :d DESC (and then :w DESC, ts DESC)

So thanks again very much.

Pfiouuu, I've learned a lot of Ruby in just one night!


--
Posted via http://www.ruby-forum.com/.

 
Reply With Quote
 
 
 
Reply

Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are Off


Similar Threads
Thread Thread Starter Forum Replies Last Post
Array of Hashes in an array of hashes - Complicated! Matt Brooks Ruby 16 09-16-2009 05:53 PM
sort Array of Hashes Adgar Marks Ruby 1 07-27-2008 02:52 AM
How to make an array of hashes to a single array with all thevalues of these hashes ? kazaam Ruby 12 09-13-2007 01:30 PM
Sort file details by consecutive categories Karl Engel Computer Support 2 03-01-2006 07:04 AM
Hash of hashes, of hashes, of arrays of hashes Tim O'Donovan Perl Misc 5 10-28-2005 05:59 AM



Advertisments
 



1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57