Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

sort on multiple attributes

18 views
Skip to first unread message

Terje Tjervaag

unread,
Oct 26, 2005, 6:56:07 AM10/26/05
to
Hi,

Just thought I'd write this to the list, since it wasn't immediately
obvious to me how to do it and I couldn't find it written anywhere on
the web..

Say you have an array of these music tracks:
Track = Struct.new("Track", :name, :artist, :album, :track_number)

.and you want to sort the array first by artist, then by album within
that artist and finally by track number within the album, this can
still be done by a pretty neat sort block:

sorted_tracks = tracks.sort do |a,b|
if a.artist != b.artist
a.artist <=> b.artist
elsif a.album != b.album
a.album <=> b.album
else
a.track_number.to_i <=> b.track_number.to_i
end
end

--
Terje


Brian Schröder

unread,
Oct 26, 2005, 7:03:45 AM10/26/05
to
On 26/10/05, Terje Tjervaag <flec...@gmail.com> wrote:
> Hi,
>
> Just thought I'd write this to the list, since it wasn't immediately
> obvious to me how to do it and I couldn't find it written anywhere on
> the web..
>
> Say you have an array of these music tracks:
> Track = Struct.new("Track", :name, :artist, :album, :track_number)
>
> ..and you want to sort the array first by artist, then by album within

> that artist and finally by track number within the album, this can
> still be done by a pretty neat sort block:
>
> sorted_tracks = tracks.sort do |a,b|
> if a.artist != b.artist
> a.artist <=> b.artist
> elsif a.album != b.album
> a.album <=> b.album
> else
> a.track_number.to_i <=> b.track_number.to_i
> end
> end
>
> --
> Terje
>
>

Or you can say
sorted_tracks = tracks.sort_by { | track |
[track.artist, track.album, track.track_number]
}

or even:

class Array
def sort_by_att(*attributes)
attributes = attributes.flatten
self.sort_by { | e | attributes.map { | a | e.send(a) } }
end
end

sorted_tracks = tracks.sort_by_att(:artist, :album, :track_number)

(warning, untested code, beware of the bug)

regards,

Brian

--
http://ruby.brian-schroeder.de/

Stringed instrument chords: http://chordlist.brian-schroeder.de/


Martin DeMello

unread,
Oct 26, 2005, 7:06:57 AM10/26/05
to
Terje Tjervaag <flec...@gmail.com> wrote:
>
> Say you have an array of these music tracks:
> Track = Struct.new("Track", :name, :artist, :album, :track_number)
>
> .and you want to sort the array first by artist, then by album within
> that artist and finally by track number within the album, this can
> still be done by a pretty neat sort block:

tracklist.sort_by {|i| [i.artist, i.album, i.track_number] }

works because ruby already defines Array#<=> to check the first entry,
break ties on the second entry, etc.

martin

Terje Tjervaag

unread,
Oct 26, 2005, 7:07:37 AM10/26/05
to
On 10/26/05, Brian Schröder <ruby....@gmail.com> wrote:
> Or you can say
> sorted_tracks = tracks.sort_by { | track |
> [track.artist, track.album, track.track_number]
> }

nice! thanks. I had a feeling it could be improved.
--
Terje


Robert Klemme

unread,
Oct 26, 2005, 7:21:11 AM10/26/05
to

Btw, IMHO you should not need track_number.to_i as they should really be
stored as numbers.

Alternative

tracks.sort do |a,b|
[:artist, :album, :track_number].each do |field|
c = a.send(field) <=> b.send(field)
return c unless c == 0
end
0
end

<shameless advertising>
I have also a related RCR pending that still can make it into the std
lib...
http://rcrchive.net/rcr/show/293
</shameless advertising>

Kind regards

robert

Terje Tjervaag

unread,
Oct 26, 2005, 8:00:13 AM10/26/05
to
On 10/26/05, Robert Klemme <bob....@gmx.net> wrote:
> Btw, IMHO you should not need track_number.to_i as they should really be
> stored as numbers.

I agree - in this case I was playing around with making the most
minimal iTunes Music Library parser I could, hence my use of .to_i.
The current 14 line program is attached, if anyone is curious. It
could be 9 if it wasn't for the error checking.

> Alternative
>
> tracks.sort do |a,b|
> [:artist, :album, :track_number].each do |field|
> c = a.send(field) <=> b.send(field)
> return c unless c == 0
> end
> 0
> end
>
> <shameless advertising>
> I have also a related RCR pending that still can make it into the std
> lib...
> http://rcrchive.net/rcr/show/293
> </shameless advertising>

I could see those additions being useful - it is a case of where to
hide the inevitable complexity I suppose, and I think your RCR deals
with it in a way that's very easy to read in the end.

--
Terje

itunes.rb
0 new messages