Elvis carried away by spaceships

I love teaching Groovy to existing Java developers, because they have such a hard time holding back Tears Of Joy when they see how much easier life can be. Today, though, I did a quick demo that resulted in a line of Groovy that was so amusing I had to post it here.

Consider a trivial POGO (Plain Old Groovy Object) called Course:

class Course
    String name
    int days
    String toString() { "($name,$days)" }

The goal was to take a collection of courses and sort it by the number of days. That’s really easy in Groovy:

def courses = [
    new Course(name:'Groovy',days:4),
    new Course(name:'Grails',days:3),
    new Course(name:'Spring',days:4),
    new Course(name:'Hibernate',days:3)

assert courses.toString() == '[(Groovy,4), (Grails,3), (Spring,4), (Hibernate,3)]'

courses.sort { it.days }

assert courses*.days == [3, 3, 4, 4]

The sort method in the java.util.Collection class is part of the Groovy JDK, meaning it’s one of the methods Groovy adds to the standard Java libraries. It takes a closure of either one or two arguments. In this case, I’m using the one-argument closure, which is used to select a property on which to base the sort. By specifying it.days in the closure, I’m telling the sort method to sort the courses based on their days property. Then I verify that the sort worked by checking that the courses are the right order, using the spread-dot operator to just look at the number of days.

In class the question that always comes up is, can I sort by days and then by name? In other words, if two courses have the same number of days, can I then sort by the name property?

That’s what the two-argument closure on the sort method is for. The two arguments are references to any pair of courses, and the closure should return a negative number, zero, or positive number according to whether the first course is less than, equal to, or greater than the second.

Here’s where things get amusing. The sort I want is:

courses.sort { a,b ->
    a.days <=> b.days ?: a.name <=> b.name

assert courses.toString() == '[(Grails,3), (Hibernate,3), (Groovy,4), (Spring,4)]'

The body of the closure on sort uses the spaceship operator <=>, which returns -1, 0, or 1 depending on whether the left side is less than, equal to, or greater than the right side. I use spaceship to compare the days properties. Then I add the Elvis operator ?: which means if the days comparison is not zero, use it, but otherwise use the following comparison, which uses another spaceship to compare by name.

It’s only after writing the code in class that one of the students pointed out that I had Elvis in between two spaceships, leading to the following observations:

  1. The spaceships are there to return Elvis to his home planet
  2. It takes two spaceships, working in tandem, to carry Elvis away, in much the same way two swallows can carry a coconut in tandem
  3. Therefore, the Elvis being carried away must be the fat Elvis from the 70s, rather than the thin, cool Elvis from the 50s

Either way, after the sort is finished, Elvis has left the building.

Thank you, thank you very much.

%d bloggers like this: