How do I find the min() or max() of two Option[Int]

Graham Lea picture Graham Lea · Sep 28, 2012 · Viewed 11.9k times · Source

How would you find minValue below? I have my own solution but want to see how others would do it.

val i1: Option[Int] = ...
val i2: Option[Int] = ...
val defaultValue: Int = ...
val minValue = ?

Answer

Travis Brown picture Travis Brown · Sep 28, 2012

Update: I just noticed that my solution below and the one in your answer behave differently—I read your question as asking for the minimum of the two values when there are two values, but in your answer you're effectively treating None as if it contained a value that's either bigger (for min) or smaller (for max) than anything else.

To be more concrete: if i1 is Some(1) and i2 is None, my solution will return the default value, while yours will return 1.

If you want the latter behavior, you can use the default semigroup instance for Option[A] and the tropical semigroup for Int. In Scalaz 7, for example, you'd write:

import scalaz._, Scalaz._

optionMonoid(Semigroup.minSemigroup[Int]).append(i1, i2) getOrElse defaultValue

Or the following shorthand:

Tags.Min(i1) |+| Tags.Min(i2) getOrElse defaultValue

It's not as clean as the applicative functor solution below, but if that's your problem, that's your problem.


Here's a more idiomatic way that doesn't involve creating an extra list:

(for { x <- i1; y <- i2 } yield math.min(x, y)) getOrElse defaultValue

Or, equivalently:

i1.flatMap(x => i2.map(math.min(x, _))) getOrElse defaultValue

What you're doing is "lifting" a two-place function (min) into an applicative functor (Option). Scalaz makes this easy with its applicative builder syntax:

import scalaz._, Scalaz._

(i1 |@| i2)(math.min) getOrElse defaultValue

The standard library solution isn't much less elegant in this case, but this is a useful abstraction to know about.