首页 文章

最小/最大,选项[T]可能为空Seq?

提问于
浏览
49

我正在做一些Scala体操,我有 Seq[T] ,其中我试图找到"smallest"元素 . 这就是我现在所做的:

val leastOrNone = seq.reduceOption { (best, current) =>
    if (current.something < best.something) current
    else best
}

它工作正常,但是对于这么简单的事情我有点长,而I don't care much for "if"s . 使用 minBy 会更优雅:

val least = seq.minBy(_.something)

...但是 minminBy 在序列为空时抛出异常 . 是否有一种惯用的,更优雅的方式来查找可能为空的列表的最小元素 Option

8 回答

  • 8

    启动 Scala 2.13minByOption / maxByOption 现在是标准库的一部分,如果序列为空,则返回 None

    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
    seq.reduceOption(_ min _)
    

    做你想要的?


    编辑:这是一个包含您的 _.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))
    

    或者,作为通用方法:

    def minOptionBy[A, B: Ordering](seq: Seq[A])(f: A => B) = 
      seq reduceOption Ordering.by(f).min
    

    你可以调用 minOptionBy(seq)(_.something)

  • 1

    与Scalaz一起使用的安全,紧凑和 O(n) 版本:

    xs.nonEmpty option xs.minBy(_.foo)
    
  • 0

    由于 O(nlogn) 复杂性,几乎没有任何更大列表的选项:

    seq.sortBy(_.something).headOption
    
  • 4

    这个怎么样?

    import util.control.Exception._
    allCatch opt seq.minBy(_.something)
    

    或者,如果您不想吞下其他例外,则更详细:

    catching(classOf[UnsupportedOperationException]) opt seq.minBy(_.something)
    

    或者,您可以使用以下内容对所有集合进行pimp:

    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)
    

    然后写下 seq.minOptionBy(_.something) .

  • 68

    我以前遇到过同样的问题,所以我扩展了Ordered并实现了compare函数 . 这是一个例子:

    case class Point(longitude0: String, latitude0: String)  extends Ordered [Point]{
    
      def this(point: Point) = this(point.original_longitude,point.original_latitude)
      val original_longitude = longitude0
      val original_latitude = latitude0
    
      val longitude = parseDouble(longitude0).get 
      val latitude = parseDouble(latitude0).get  
    
      override def toString: String = "longitude: " +original_longitude +", latitude: "+ original_latitude
    
      def parseDouble(s: String):  Option[Double] = try { Some(s.toDouble) } catch { case _ => None }
    
      def distance(other: Point): Double =
        sqrt(pow(longitude - other.longitude, 2) + pow(latitude - other.latitude, 2))
    
     override def compare(that: Point): Int = {
      if (longitude < that.longitude)
        return -1
      else if (longitude == that.longitude && latitude < that.latitude)
        return -1
      else
        return 1
     }
    }
    

    所以,如果我有一个Point的seq,我可以要求max或min方法

    var points =  Seq[Point]()
    
    val maxPoint = points.max
    val minPoint = points.min
    
  • -3

    你总是可以这样做:

    case class Foo(num: Int)
    
    val foos: Seq[Foo] = Seq(Foo(1), Foo(2), Foo(3))
    val noFoos: Seq[Foo] = Seq.empty
    
    def minByOpt(foos: Seq[Foo]): Option[Foo] =
      foos.foldLeft(None: Option[Foo]) { (acc, elem) => 
        Option((elem +: acc.toSeq).minBy(_.num)) 
      }
    

    然后使用像:

    scala> minByOpt(foos)
    res0: Option[Foo] = Some(Foo(1))
    
    scala> minByOpt(noFoos)
    res1: Option[Foo] = None
    
  • 9

    在Haskell中,你将minimumBy调用为

    least f x | Seq.null x = Nothing
              | otherwise  = Just (Seq.minimumBy f x)
    

相关问题