Scala - Min/max с опцией [T] для возможного пустого Seq?
Я делаю немного Scala гимнастики, где у меня Seq[T]
, в котором я пытаюсь найти "самый маленький" элемент. Это то, что я делаю прямо сейчас:
val leastOrNone = seq.reduceOption { (best, current) =>
if (current.something < best.something) current
else best
}
Это прекрасно работает, но я не совсем доволен - он немного длинный для такой простой вещи, и Мне наплевать на "если" s. Использование minBy
было бы намного более элегантным:
val least = seq.minBy(_.something)
... но min
и minBy
бросать исключения, когда последовательность пуста. Есть ли идиоматический, более элегантный способ нахождения наименьшего элемента, возможно, пустого списка как Option
?
Ответы
Ответ 1
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)
Ответ 2
Безопасная, компактная и O(n)
версия с Scalaz:
xs.nonEmpty option xs.minBy(_.foo)
Ответ 3
Вряд ли опция для любого большего списка из-за сложности O(nlogn)
:
seq.sortBy(_.something).headOption
Ответ 4
Scala позволяет зафиксировать ошибку с помощью Try
. Пусть написана функция, которая ее использует:
def min[T <% Ordered[T]](s: Seq[T]) = util.Try(s.min).toOption
Теперь давайте проверим, что:
scala> min(Seq(1,2,3))
res4: Option[Int] = Some(1)
scala> min(Seq.empty[Int])
res5: Option[Int] = None
Ответ 5
Как насчет этого?
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)
.
Ответ 6
У меня такая же проблема раньше, поэтому я расширяю 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
}
}
поэтому, если у меня есть seq Point
Я могу попросить метод max или min
var points = Seq[Point]()
val maxPoint = points.max
val minPoint = points.min
Ответ 7
В Haskell вы завершите вызов minimumBy
как
least f x | Seq.null x = Nothing
| otherwise = Just (Seq.minimumBy f x)