63

我正在做一些 Scala 体操,Seq[T]我试图在其中找到“最小”元素。这就是我现在所做的:

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

它工作正常,但我不太满意 - 这么简单的事情有点长,我不太关心 "if"。使用minBy会更优雅:

val least = seq.minBy(_.something)

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

4

10 回答 10

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

于 2012-06-06T21:35:42.030 回答
35

开始Scala 2.13, minByOption/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
于 2018-10-02T20:50:56.200 回答
8

一个安全、紧凑O(n)的 Scalaz 版本:

xs.nonEmpty option xs.minBy(_.foo)
于 2015-02-24T08:01:03.007 回答
5

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

seq.sortBy(_.something).headOption
于 2012-06-06T21:27:28.213 回答
3

此外,它可以这样做

Some(seq).filter(_.nonEmpty).map(_.minBy(_.something))
于 2019-11-26T19:22:39.697 回答
1

这个怎么样?

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

或者,更冗长,如果您不想吞下其他异常:

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

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

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)

于 2012-06-06T21:18:07.120 回答
1

我之前也有同样的问题,所以我扩展了 Ordered 并实现了比较功能。这是示例:

 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
 }
}

所以如果我有一个点序列,我可以要求 max 或 min 方法

  var points =  Seq[Point]()

val maxPoint = points.max
val minPoint = points.min
于 2017-08-27T13:22:08.590 回答
1

你总是可以做类似的事情:

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
于 2018-11-28T03:45:13.567 回答
1

对于 scala < 2.13

Try(seq.minBy(_.something)).toOption

对于 Scala 2.13

seq.minByOption(_.something)
于 2019-10-31T09:21:14.647 回答
-4

在 Haskell 中,您将minimumBy调用包装为

least f x | Seq.null x = Nothing
          | otherwise  = Just (Seq.minimumBy f x) 
于 2012-06-06T21:07:00.203 回答