Search code examples
scalasortingoptional-values

Scala - Sort by Optional fields, with None value last


I have the following class :

final case class PowerDetails (
  [...],
  label: String,
  title: String,
  category: String,
  idCategory: Option[String],
  numCategory: Option[Int]
)

I need to sort a list of PowerDetails like this :

Elements having a value for idCategory and numCategory should be before the None , and ordered by idCategory and then numCategory. As for the None values, they need to be ordered by the fields category , title , label.

If possible, I would like to avoid extending Ordered

final case class PowerDetails (

   ...

) extends Ordered[PowerDetails] {

    override def compare(that: PowerDetails): Int = {
       // Define here the comparison
    }

}

I tried creating an Ordering as below and then use the sorted function, but I'm getting a NullPointerException

  implicit val orderingPowers = optionOrdering.thenComparing(ordering)

  val ordering: Ordering[PowerDetails] = Ordering[(String, String, String)]
    .on[PowerDetails](powerDetail =>
      ( powerDetail.category, powerDetail.title, powerDetail.label))

  val optionOrdering : Ordering[PowerDetails] = Ordering[(Option[String], Option[Int])]
    .on[PowerDetails](powerDetail =>
      (powerDetail.idCategory, powerDetail.numCategory))

Could you please help me find out how to do it ? Thanks.


Solution

  • Here's another approach also using Ordering.by along with Option's isEmpty, leveraging false < true for the wanted Some/None ordering:

    final case class PowerDetails (
      field1: String,
      field2: Int,
      label: String,
      title: String,
      category: String,
      idCategory: Option[String],
      numCategory: Option[Int]
    )
    
    implicit val PDOrderer: Ordering[PowerDetails] = Ordering.by{
      case PowerDetails(_, _, label, title, cat, idCat, numCat) =>
        (idCat.isEmpty, idCat, numCat.isEmpty, numCat, cat, title, label)
    }