leyren
leyren

Reputation: 534

Scala sorting of case classes by type

Given some case classes which extend a common trait, I want to be able to define an Ordering (actually not necessarily, the goal is to sort them in that manner) dependent on their types. For example:

  sealed trait Element
  case class A(x: Int) extends Element
  case class B(x: Int) extends Element
  case class C(x: Int) extends Element
  case class D(x: Int) extends Element
  case class E(x: Int) extends Element
  case class F(x: Int) extends Element

  val elements: List[Element] = List(
    A(5), F(3), E(1), C(19), A(3), F(1)
  )

with an ordering of F -> A -> all other cases, such that the resulting list is List(F(3), F(1), A(5), A(3), E(1), C(19)). The ordering between elements of the same type does not matter.

I came up with multiple different solutions but they all seem convoluted, and I just feel like I'm missing some obvious way of accomplishing this. This is how I implemented it using an ordering:

  val sorted = elements.sorted{(a: Element, b: Element) => (a, b) match {
      case (_: F, _: F) =>  0
      case (_: F, _   ) => -1
      case (_   , _: F) =>  1
      case (_: A, _: A) =>  0
      case (_: A, _   ) => -1
      case (_   , _: A) =>  1
      case _            =>  0
    }
  }

However this would obviously scale horribly, and is not nice to look at..

Upvotes: 0

Views: 903

Answers (2)

Tim
Tim

Reputation: 27356

Just define an implicit Ordering for Element like this:

object Element {
  implicit val ord: Ordering[Element] = Ordering.by {
    case _: F => 0
    case _: A => 1
    case _ => 2
  }
}

Then elements.sorted will give you the answer you want.

Upvotes: 7

Andronicus
Andronicus

Reputation: 26046

How about introducing a class, that will hold the priority to sort the elements, for example:

class Prio(val prio: Int) {}
case class A() extends Prio(5)
case class B() extends Prio(3)
case class C() extends Prio(9)
case class D() extends Prio(7)

object Prio {
  def main(args: Array[String]): Unit = {
    List(A(), B(), C(), D())
      .sortBy(_.prio)
      .foreach(println)
  }
}

This gives the following result:

B()
A()
D()
C()

You can of course mix in other interfaces, add values to the type classes. The Prio class can be even enriched in more priorities that can be used in different situations.

Since you only want to have A as first and F as last, you can use the following ordering:

class Prio(val prio: Int) {}
case class A() extends Prio(-1)
case class B() extends Prio(0)
case class C() extends Prio(0)
case class D() extends Prio(0)
case class E() extends Prio(0)
case class F() extends Prio(1)

Upvotes: 3

Related Questions