I\'m doing a bit of Scala gymnastics where I have Seq[T]
in which I try to find the \"smallest\" element. This is what I do right now:
val leastOrNo
Starting Scala 2.13
, minByOption/maxByOption
is now part of the standard library and returns None
if the sequence is empty:
seq.minByOption(_.something)
List((3, 'a'), (1, 'b'), (5, 'c')).minByOption(_._1) // Option[(Int, Char)] = Some((1,b))
List[(Int, Char)]().minByOption(_._1) // Option[(Int, Char)] = None
Also, it is available to do like that
Some(seq).filter(_.nonEmpty).map(_.minBy(_.something))
How about this?
import util.control.Exception._
allCatch opt seq.minBy(_.something)
Or, more verbose, if you don't want to swallow other exceptions:
catching(classOf[UnsupportedOperationException]) opt seq.minBy(_.something)
Alternatively, you can pimp all collections with something like this:
import collection._
class TraversableOnceExt[CC, A](coll: CC, asTraversable: CC => TraversableOnce[A]) {
def minOption(implicit cmp: Ordering[A]): Option[A] = {
val trav = asTraversable(coll)
if (trav.isEmpty) None
else Some(trav.min)
}
def minOptionBy[B](f: A => B)(implicit cmp: Ordering[B]): Option[A] = {
val trav = asTraversable(coll)
if (trav.isEmpty) None
else Some(trav.minBy(f))
}
}
implicit def extendTraversable[A, C[A] <: TraversableOnce[A]](coll: C[A]): TraversableOnceExt[C[A], A] =
new TraversableOnceExt[C[A], A](coll, identity)
implicit def extendStringTraversable(string: String): TraversableOnceExt[String, Char] =
new TraversableOnceExt[String, Char](string, implicitly)
implicit def extendArrayTraversable[A](array: Array[A]): TraversableOnceExt[Array[A], A] =
new TraversableOnceExt[Array[A], A](array, implicitly)
And then just write seq.minOptionBy(_.something)
.
seq.reduceOption(_ min _)
does what you want?
Edit: Here's an example incorporating your _.something
:
case class Foo(a: Int, b: Int)
val seq = Seq(Foo(1,1),Foo(2,0),Foo(0,3))
val ord = Ordering.by((_: Foo).b)
seq.reduceOption(ord.min) //Option[Foo] = Some(Foo(2,0))
or, as generic method:
def minOptionBy[A, B: Ordering](seq: Seq[A])(f: A => B) =
seq reduceOption Ordering.by(f).min
which you could invoke with minOptionBy(seq)(_.something)