This sorting technique is one I’ve had a chance to use at work more lately. But what keeps tripping me up is when you use the block to sorting primarily by one field, with a secondary sort on another field. Let’s say Fish has species and type, and we have these fish in our database:
| species | type |
|---|---|
| Platy | Sunset |
| Platy | Calico |
| Molly | Dalmation |
| Platy | Rainbow |
| Guppy | Fancy Tail |
| Platy | Mickey Mouse |
When I sort first by species, then by type, I keep accidentally doing the following, which gives the wrong sorted results:
>> fishes = Fish.find(:all) >> fishes.sort do |a,b| ?> a.species <=> b.species >> a.type <=> b.type >> end
Doing that, I end up with a list like:
| species | type |
|---|---|
| Platy | Calico |
| Molly | Dalmation |
| Guppy | Fancy Tail |
| Platy | Mickey Mouse |
| Platy | Rainbow |
| Platy | Sunset |
It ignores my first sort on species, and ends up sorting only by type!
Why? What’s wrong with that? Well, the <=> comparator function (also known informally as the “spaceship operator”) returns either -1, 0 or 1, depending on whether the first value is less than, equal to, or greater than the other. The block will return the last statement evaluated. So what happens is we compare species, then we then compare type, and it is always the result of the type comparison, -1, 0 or 1, is returned from the block. The problem is, if species is not equal, then we want to stop there and return -1 or 1 accordingly and not evaluate type at all.
A simple way to do this is to add “if result==0″ to the end of the type comparison, and only evaluate type if species was equal.
>> fishes = Fish.find(:all) >> fishes.sort do |a,b| ?> result = a.species <=> b.species >> result = a.type <=> b.type if result == 0 >> result >> end
This way, it will perform the first search by species, then only continue to perform the secondary search if the result of the first search was zero, that is they were equal values. And so I end up with a list like:
| species | type |
|---|---|
| Guppy | Fancy Tail |
| Molly | Dalmation |
| Platy | Calico |
| Platy | Mickey Mouse |
| Platy | Rainbow |
| Platy | Sunset |

