A Fresh Cup is Mike Gunderloy's software development weblog, covering Ruby on Rails and whatever else I find interesting in the universe of software. I'm a full-time Rails developer and contributor, available for long- or short-term consulting, with solid experience in working as part of a distributed team. If you'd like to hire me, drop me a line. I'm also the author of Rails Rescue Handbook and Rails Freelancing Handbook.

Navigation
« Double Shot #340 | Main | Double Shot #339 »
Tuesday
Nov252008

Sorting ActiveRecord Objects

I recently hit a situation where I needed to sort an array of ActiveRecord objects on a particular attribute. The catch was that in this case the array started out with the results of a find operation - but then it had a bunch more transient objects added to it that weren't part of the database. Fortunately the Array#sort method makes short work of this. Given an array a of objects with an entry_date attribute:

[sourcecode language='ruby']
a.sort! {|x,y| x.entry_date <=> y.entry_date}
[/sourcecode]

Because this was the only sort I needed on this particular model, I decided to push the operation right down into the model:

[sourcecode language='ruby']
class Receipt < ActiveRecord::Base
def <=> (other)
entry_date <=> other.entry_date
end
end
[/sourcecode]

Then the sort is much simpler:

[sourcecode language='ruby']
a.sort!
[/sourcecode]

Note that this technique only makes sense if your array isn't coming straight from the database. If you are retrieving records from the database, you're better off including an :order clause in your finder to let the database do the sorting.

Reader Comments (2)

I would rather use #sort_by (if making another array is ok) or something like this:

a.sort! &Comparator.of(:entry_date)

with

class Comparator < Proc
def self.of(*attributes)
new(*attributes)
end

def initialize(*attributes)
@attributes = attributes
@direction_modifier = 1
end

def call(*args)
# handle invalid args.size
a, b = args[0], args[1]
@attributes.each do |attr|
result = a.send(attr) b.send(attr)
return result*direction_modifier if result != 0
end
return 0
end

def reversed
@direction_modifier = -@direction_modifier
end
end

This will allow comparing multiple fields, like
Comparator.of(:lastname, :firstname)
or
Comparator.of(:lastname, :firstname).reversed

November 26, 2008 | Unregistered CommenterMaxim Kulkin

Or you could do Receipt.find(:all).sort_by(&:entry_date)

November 30, 2008 | Unregistered CommenterRyan Bigg

PostPost a New Comment

Enter your information below to add a new comment.
Author Email (optional):
Author URL (optional):
Post:
 
Some HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>