Skip to Content
Course content

65: Ordering and Ordered Traits

Click on the "Edit" button in the top corner of the screen to edit your slide content.

How do I make my custom classes work with comparison operators like < and >?

If you've ever tried to sort a list of custom case classes, you've probably hit a wall where Scala tells you it doesn't know how to compare them. To fix this, you mix in the Ordered[T] trait. When you do this, you're essentially telling Scala: "This class knows how to compare itself to another instance of the same type."

The only thing you have to do is implement the compare method. It returns an integer: negative if this object is "less than" the other, zero if they're equal, and positive if it's "greater than".

case class Book(title: String, pages: Int) extends Ordered[Book] {
  def compare(that: Book): Int = this.pages - that.pages
}

val book1 = Book("Scala Guide", 300)
val book2 = Book("Quick Start", 100)

println(book1 > book2) // true
println(book2 < book1) // true

By implementing Ordered, you get <, >, <=, and >= for free. It's a huge quality-of-life improvement for your code.

Wait, is Ordered the same thing as Ordering?

This is where most people get tripped up. They look similar, but they serve different purposes. Think of Ordered as "internal" and Ordering as "external."

Ordered is a trait you mix into your class. It defines the natural ordering of that object. But what happens if you don't own the class? You can't just go into the Scala standard library and add extends Ordered to the String or Int classes. That's where Ordering[T] comes in.

Ordering is a separate object that knows how to compare two other objects. It's a "type class" pattern. If you look at the signature of List.sorted, it doesn't actually require the elements to be Ordered; it asks for an implicit Ordering to be available in the scope.

What if I need to sort the same data in different ways?

The problem with Ordered is that you can only have one natural ordering. If you've defined Book to be ordered by page count, you're stuck with that. But in a real app, you'll probably want to sort books by title, then by author, then by date published.

This is where I always reach for Ordering. You can define multiple Ordering objects for the same type and just pass the one you need to the sorting method.

case class Book(title: String, pages: Int)

object BookOrderings {
  implicit val byPages: Ordering[Book] = Ordering.by(_.pages)
  implicit val byTitle: Ordering[Book] = Ordering.by(_.title)
}

import BookOrderings._

val library = List(Book("Zebra", 10), Book("Apple", 500), Book("Mantis", 100))

// Use byPages (because it's the first implicit found or explicitly passed)
println(library.sorted(byPages)) 
// Use byTitle
println(library.sorted(byTitle))

I personally prefer Ordering.by because it's concise. You don't have to write the manual subtraction or comparison logic; you just tell Scala which field to look at.




📋 Practical Task

Implement a Multi-Criteria Product Sorter

You are building a small inventory system for an electronics store. You have a Product case class with name: String, price: Double, and rating: Double.

Your task is to implement the following:

  1. Make Product extend Ordered[Product] so that the "natural order" is based on price (cheapest first).
  2. Create a separate Ordering[Product] (perhaps inside a companion object) that sorts products by rating in descending order (highest rating first).
  3. In your main method, create a list of 3-4 products and demonstrate both sorting methods: one using the natural .sorted call and one using your custom rating-based Ordering.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.