私はScala体操Seq[T]
ここで、「最小の」要素を見つけようとします。これは私が今やっていることです:
val leastOrNone = seq.reduceOption { (best, current) =>
if (current.something < best.something) current
else best
}
それはうまく動作しますが、私は完全に満足していません-そのような単純なことのために少し長いです、そして、 「if」についてはあまり気にしません 。 minBy
を使用すると、はるかにエレガントになります。
val least = seq.minBy(_.something)
...ただし、シーケンスが空の場合、min
およびminBy
は例外をスローします。おそらく空のリストの最小要素をOption
として見つける、慣用的でよりエレガントな方法はありますか?
開始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
_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)
で呼び出すことができます
Scalazを使用した安全でコンパクトなO(n)
バージョン:
xs.nonEmpty option xs.minBy(_.foo)
O(nlogn)
の複雑さのため、大きなリストのオプションはほとんどありません。
seq.sortBy(_.something).headOption
これはどう?
_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)
と書くだけです。
あなたはいつも次のようなことをすることができます:
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
前と同じ問題があるので、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
}
}
したがって、ポイントのシーケンスがある場合、最大または最小メソッドを要求できます
var points = Seq[Point]()
val maxPoint = points.max
val minPoint = points.min