Подтвердить что ты не робот

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?

4b9b3361

Ответ 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)