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/
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
nice! thanks. I had a feeling it could be improved.
--
Terje
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
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