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

13,362

Solution 1

I think this is what you're after:

val minValue = List(i1, i2).flatten match {
  case Nil => defaultValue
  case xs => xs.min
}

I'd avoid sorted since sorting requires a lot more processing than simply finding the max or min (although it probably doesn't make much difference in this case).

Solution 2

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.

Solution 3

I solved a similar problem using the following approach. We handle a special case when both of the options have values, otherwise we use an API method Option.orElse.

val a: Option[Int]  = Some(10)
val b: Option[Int] = Some(20)
val c: Option[Int] = (a, b) match {
  case (Some(x), Some(y)) => Some(x min y)
  case (x, y) => x orElse y
}

Solution 4

val minValue: Int = List(i1, i2).flatten.sorted.headOption getOrElse defaultValue

Solution 5

You can use patterns in for expressions, values that do not match the pattern are discarded.

(for (Some(x) <- List(None, Some(3))) yield x) max

Not as good as the List.flatten approach though.

Share:
13,362
Graham Lea
Author by

Graham Lea

Want to understand your software architecture better? You should check out Archium! Or you can just read more about me.

Updated on June 05, 2022

Comments

  • Graham Lea
    Graham Lea about 2 years

    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 = ?
    
  • Graham Lea
    Graham Lea over 11 years
    Thanks for the contribution, but I wanted the min value if either one was defined, not only when both are defined.
  • Graham Lea
    Graham Lea over 11 years
    Thanks for your answer, Travis. It's pleasing in its brevity, though there's two things that turn me off using it: it uses a third party library, and in order to understand it I need to read an article about "a relatively new area in mathematics and algebraic geometry".
  • Graham Lea
    Graham Lea over 11 years
    I think this is probably the best way, though it's unfortunate that the idiomatic style makes it four lines long. I tried to shorten it (by removing match) and came up with ((xs:List[Int]) => {if (xs == Nil) defaultValue else xs.min})(List(i1, i2).flatten) but this is clearly obfuscation for the sake of brevity.
  • user40171
    user40171 almost 8 years
    why not List(Option(defaultValue), i1, i2).flatten.min ?
  • Luis Ramirez-Monterosa
    Luis Ramirez-Monterosa over 7 years
    flatten.min would throw an UnsupportedOperationException with empty.min if List is empty, when you flatten you can end up with an empty list if all elements inside the list are None
  • Boris Azanov
    Boris Azanov about 3 years
    I added a little more actual way using cats for that.