如何在Scala中实现具有多个max的maxBy

时间:2016-01-14 20:28:29

标签: scala

我需要一个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)
        }
      }
    )
  }

有更好的方法吗?

2 个答案:

答案 0 :(得分:3)

(1)Ordering#compare承诺用负数,正数或零数表示三种可能的结果,而不是-1,1或0。

(2)Option#fold generally(虽然不是普遍的)被认为比模式匹配更惯用。

(3)您每个元素可能多次调用fTraversableOnce#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)