Scala Ordered by multiple fields

scala

Solution

Option 1: sortBy

Using the `sortBy` method, this can be pretty simple:

case class Person(first: String, middle: String, last: String)
val personList = List(Person("john", "a", "smith"), Person("steve", "x", "scott"), Person("bill", "w", "smith"))
personList.sortBy{ case Person(f,m,l) => (l,f,m) } 

Option 2: Extend Ordered[Person]

By extending `Ordered[Person]`, the class will know how to sort itself, so we get things like `sorted`, `min`, and `max` for free:

case class Person(first: String, middle: String, last: String) extends Ordered[Person] {
  def compare(that: Person): Int =
    (last compare that.last) match {
      case 0 => 
        (first compare that.first) match {
          case 0 => middle compare that.middle
          case c => c
        }
      case c => c
    }
}

val personList = List(Person("john", "a", "smith"), Person("steve", "x", "scott"), Person("bill", "w", "smith"))
personList.sorted
personList.min
personList.max

Option 3: Implicit Ordering

If you use an implicit `Ordering`, then you get `sorted`, `min`, etc without having that particular ordering tied to your original class. This decoupling might be convenient, or it might by annoying, depending on your specific case.

case class Person(first: String, middle: String, last: String)

implicit val ord = new Ordering[Person] { 
  def compare(self: Person, that: Person): Int =
    (self.last compare that.last) match {
      case 0 => 
        (self.first compare that.first) match {
          case 0 => self.middle compare that.middle
          case c => c
        }
      case c => c
    }
}

val personList = List(Person("john", "a", "smith"), Person("steve", "x", "scott"), Person("bill", "w", "smith"))
personList.sorted
personList.min
personList.max

Problem

This question is probably quite dumb, but I can't find an example and can't figure it out. I want to compare two `Person` classes by last, first, and middle name, in that order. Here's the brain-dead way to do it: ``` def compare(that: Person): Int = { val last: Int = lastName.compare(that.lastName) if (last != 0) last else { val first: Int = firstName.compare(that.firstName) if (first != 0) first else middleName.compare(that.middleName) } ``` I know there's some much more clever way to do this (probably using `Ordering`) but I can't put my finger on it. Todd I figured out this once I realized how to access the right things in Ordering. ``` def compare(that: Person): Int = { Ordering.Tuple3(Ordering.String, Ordering.String, Ordering.String).compare( (lastName, firstName, middleName), (that.lastName, that.firstName, that.middleName)) } ``` I'm pretty sure I can get away with fewer explicits, but this works and is reasonably compact.

Original source

Related problems