我需要一个maxBy,在相等的情况下返回所有最大值。
这是签名和第一个实现:
def maxBy[A, B](as: Seq[A])(f: A => B)(implicit cmp: Ordering[B]) : Seq[A] =
as.groupBy(f).toList.maxBy(_._1)._2
示例:
maxBy(Seq(("a", "a1"),("a", "a2"),("b", "b1"),("b", "b2")))(_._1)
res6: Seq[(String, String)] = List(("b", "b1"), ("b", "b2"))
更新了@thearchetypepaul评论
def maxBy[A, B](l: Seq[A])(f: A => B)(implicit cmp: Ordering[B]) : Seq[A] = {
l.foldLeft(Seq.empty[A])((b, a) =>
b.headOption match {
case None => Seq(a)
case Some(v) => cmp.compare(f(a), f(v)) match {
case -1 => b
case 0 => b.+:(a)
case 1 => Seq(a)
}
}
)
}
有更好的方法吗?
答案 0 :(得分:3)
(1)Ordering#compare
承诺用负数,正数或零数表示三种可能的结果,而不是-1,1或0。
(2)Option#fold
generally(虽然不是普遍的)被认为比模式匹配更惯用。
(3)您每个元素可能多次调用f
。 TraversableOnce#maxBy
曾经在2.11 fixed之前执行此操作。
(4)您只接受Seq
。 Scala库很难使用CanBuildFrom
来概括算法;你可能也想。
(5)如果您愿意,可以使用句法糖B : Ordering
。
(6)你前置Seq
。这比追加更快,因为List
的前置是O(1),而附加是O(n)。但是你以相反的顺序结束了结果。 foldRight
会更正此问题。 (或者您可以在最终结果上致电reverse
。)
如果您想允许使用CanBuildFrom
,
def maxBy[A, Repr, That, B : Ordering](elements: TraversableLike[A, Repr])(f: A => B)(implicit bf: CanBuildFrom[Repr, A, That]): That = {
val b = bf()
elements.foldLeft(Option.empty[B]) { (best, element) =>
val current = f(element)
val result = best.fold(0)(implicitly[Ordering[B]].compare(current, _))
if (result > 0) {
b.clear()
}
if (result >= 0) {
b += element
Some(current)
} else {
best
}
}
b.result
}
如果您想使用TraversableOnce
,
def maxBy[A, B : Ordering](elements: TraversableOnce[A])(f: A => B): Seq[A] = {
elements.foldRight((Option.empty[B], List.empty[A])) { case (element, (best, elements)) =>
val current = f(element)
val result = best.fold(0)(implicitly[Ordering[B]].compare(current, _))
if (result > 0) {
(Some(current), List(element))
} else {
(best, if (result == 0) element +: elements else elements)
}
}._2
}
答案 1 :(得分:0)
如果数据集很小,那么性能就不会引起您太多关注
您就可以排序,反向和获取内容。
def maxBy[A,B:Ordering](l:List[A], f: A => B): List[A] = {
l.sortBy(f).reverse match {
case Nil => Nil
case h :: t => h :: t.takeWhile(x => f(x) == f(h))
}
}
其中f
应该是A上的同构。
而且您还可以在比较之前保存f(h)