My personal favorite method is to make use of the provided implicit ordering for Tuples, as it is clear, concise, and correct:
case class A(tag: String, load: Int) extends Ordered[A] {
// Required as of Scala 2.11 for reasons unknown - the companion to Ordered
// should already be in implicit scope
import scala.math.Ordered.orderingToOrdered
def compare(that: A): Int = (this.tag, this.load) compare (that.tag, that.load)
}
This works because the companion to Ordered
defines an implicit conversion from Ordering[T]
to Ordered[T]
which is in scope for any class implementing Ordered
. The existence of implicit Ordering
s for Tuple
s enables a conversion from TupleN[...]
to Ordered[TupleN[...]]
provided an implicit Ordering[TN]
exists for all elements T1, ..., TN
of the tuple, which should always be the case because it makes no sense to sort on a data type with no Ordering
.
The implicit ordering for Tuples is your go-to for any sorting scenario involving a composite sort key:
as.sortBy(a => (a.tag, a.load))
As this answer has proven popular I would like to expand on it, noting that a solution resembling the following could under some circumstances be considered enterprise-grade™:
case class Employee(id: Int, firstName: String, lastName: String)
object Employee {
// Note that because `Ordering[A]` is not contravariant, the declaration
// must be type-parametrized in the event that you want the implicit
// ordering to apply to subclasses of `Employee`.
implicit def orderingByName[A <: Employee]: Ordering[A] =
Ordering.by(e => (e.lastName, e.firstName))
val orderingById: Ordering[Employee] = Ordering.by(e => e.id)
}
Given es: SeqLike[Employee]
, es.sorted()
will sort by name, and es.sorted(Employee.orderingById)
will sort by id. This has a few benefits:
- The sorts are defined in a single location as visible code artifacts. This is useful if you have complex sorts on many fields.
- Most sorting functionality implemented in the scala library operates using instances of
Ordering
, so providing an ordering directly eliminates an implicit conversion in most cases.