Min/max with Option[T] for possibly empty Seq?

后端 未结 10 952
别那么骄傲
别那么骄傲 2021-01-31 14:03

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         


        
相关标签:
10条回答
  • 2021-01-31 14:29

    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
    
    0 讨论(0)
  • 2021-01-31 14:35

    Also, it is available to do like that

    Some(seq).filter(_.nonEmpty).map(_.minBy(_.something))
    
    0 讨论(0)
  • 2021-01-31 14:36

    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).

    0 讨论(0)
  • 2021-01-31 14:37
    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)

    0 讨论(0)
提交回复
热议问题