Sometimes you need to get the first element of a list. No problem: List.head to the rescue, right? But what happens when you call List.head on an empty list?
val xs = List[Int]() val h = xs.head // java.util.NoSuchElementException: head of empty list
Well that’s not good. You could get around that little wart with this:
val xs = List[Int]()
val h = xs match {
case h :: _ => h
case Nil => -1
}
That’s not great. Alternatively, there’s this:
val xs = List[Int]() val h = if (xs.isEmpty) -1 else xs.head // h: Int = -1
Yeah, I’m not wild about those options either.
Speaking of options, what if you had a method that returns a None if you ask for the head of an empty list? If the list is not empty, it could return a Some containing the value of the head element. List.headOption does just that.
val empty = List[Int]() val nonempty = (9 to 17).toList val nuthin = empty.headOption // nuthin: Option[Int] = None val sumthin = nonempty.headOption // sumthin: Option[Int] = Some(9)
Now you can use Option.getOrElse on the result of a call to List.headOption in order to return a default value in the event of an empty list:
val empty = List[Int]() val nonempty = (9 to 17).toList val head = nonempty.headOption getOrElse -1 // head: Int = 9 val fallback = empty.headOption getOrElse -1 // fallback: Int = -1
Now when might you actually use something like this? Perhaps you want to determine the top salesman each day, but only if the salesman has reached a certain threshold, say, $10,000. You can filter out the salesmen who don’t reach the threshold, sort the list of salesmen according to end-of-day sales totals, and then try to take the head element. If no one makes the cut, then the filter operation returns an empty list, which ultimately yields a None.
case class Salesman(name: String, sales: BigDecimal)
def findTopSalesman (salesmen : List[Salesman]) = {
salesmen.filter { _.sales >= 10000 }
.sortBy { -_.sales } // descending
.headOption
}
So then, if Monday’s sales are as follows, then no one gets the prize because no one has broken $10,000:
val monday = List(
Salesman("Joe Bob", 9500),
Salesman("Sally Jane", 8500),
Salesman("Betty Lou", 9800),
Salesman("Sammy Joe", 6500)
)
val mondayTop = findTopSalesman(monday)
// mondayTop: Option[Salesman] = None
On Tuesday, though, there are two contenders. Alas, there can be only one winner, and Sally Jane (who, ironically, is from British Columbia) takes the prize:
val tuesday = List(
Salesman("Joe Bob", 9500),
Salesman("Sally Jane", 18500),
Salesman("Betty Lou", 11800),
Salesman("Sammy Joe", 6500)
)
val tuesdayTop = findTopSalesman(tuesday)
// tuesdayTop: Option[Salesman] =
// Some(Salesman(Sally Jane,18500))
One reply on “Scala Saturday – List.headOption”
[…] week, we looked at List.headOption. If you don’t need the first item in a list, but rather the last item, the counterpart of […]