(1) Ordering#compare promises to denote the three possible results by a negative, positive, or zero number, not -1, 1, or 0 necessarily.
(2) Option#fold is generally (though not universally) considered to be more idiomatic than pattern matching.
(3) You are calling f possibly multiple times per element. TraversableOnce#maxBy used to do this before it was fixed in 2.11.
(4) You only accept Seq. The Scala library works hard to use CanBuildFrom to generalize the algorithms; you might want to as well.
(5) You can use the syntactic sugar B : Ordering if you would like.
(6) You prepend to the Seq. This is faster than appending, since prepending is O(1) for List and appending is O(n). But you wind up with the results in reverse order. foldRight will correct this. (Or you can call reverse on the final result.)
If you want to allow the use of 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
}
If you want to work with 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
}
f(v), so to not repeatedly callfon the same value many times.bcan beNoneonly the first time through. So check forl.isEmptyonce outside thefoldLeftthenl.tail.foldLeft(l.head){...}and the outer match isn't needed. Then make the accumulator a pair of the list so far, andf(v)so you don't have to recalculatef(v)each time.