17

在集合中找到最常见/最常见元素的最佳方法是什么?例如:

list = List(1, 3, 4, 4, 2)
list.mostCommon   // => 4        !! This is what I want !!

嗯..一个人可以做的是先做一个groupBy,然后再做map一个length,然后选择最大的一个。那么你会得到:

Map(1 -> List(1), 4 -> List(4, 4), 3 -> List(3), 2 -> List(2))
(...)
Map(1 -> 1, 4 -> 2, 3 -> 1, 2 -> 1)  // mapped by length. 4 -> 2  since there's two 4s

最后,选择4映射到最高数字 ( ) 的键 ( 2)。(嵌套问题:最好的方法是什么?)。但是对于这样一个简单的操作来说,这似乎需要做很多工作..?

有没有更好/更惯用的方法来做到这一点?

4

5 回答 5

26

我不得不说:

list.groupBy(identity).mapValues(_.size).maxBy(_._2)._1

要不就:

list.groupBy(identity).maxBy(_._2.size)._1

对我来说,这似乎并没有那么多工作。

如果您担心只需要计数时为每个值构建列表的开销,您可以执行以下操作:

list.foldLeft(Map.empty[Int, Int].withDefaultValue(0)) {
  case (m, v) => m.updated(v, m(v) + 1)
}.maxBy(_._2)._1

甚至可以随时跟踪最大值,以避免最后的额外遍历:

list.foldLeft(
  Map.empty[Int, Int].withDefaultValue(0), -1 -> Double.NegativeInfinity
) {
  case ((m, (maxV, maxCount)), v) =>
    val count = m(v) + 1
    if (count > maxCount) (m.updated(v, count), v -> count)
    else (m.updated(v, count), maxV -> maxCount)
}._2._1

不过,这显然比上面的单行代码可读性要差得多,所以我建议坚持使用它们,除非您可以证明(即,通过基准测试,而不是推测)它们是您应用程序的瓶颈。

于 2012-12-14T12:07:54.980 回答
3

从 开始Scala 2.13,我们可以使用:

List(1, 3, 4, 4, 2, 3)
  .groupMapReduce(identity)(_ => 1)(_+_).maxByOption(_._2).map(_._1)
// Option[Int] = Some(4)

这:

  • groups 项(MapReduce 的组部分)

  • maps 每个分组值出现为 1(映射组Map Reduce 的一部分)

  • reduces 值在一组值 ( _ + _) 中,通过对它们求和(减少 groupMap Reduce的一部分)。

  • 最后通过 nbr 的出现次数获得可选的最大值并将其映射以获取相应的项目。


如果您知道您的列表不为空,那么简单的maxBy也可以:

List(1, 3, 4, 4, 2, 3).groupMapReduce(identity)(_ => 1)(_+_).maxBy(_._2)._1
// 4

该部分是通过以下顺序一次执行groupMapReduce的等效版本:

List(1, 3, 4, 4, 2, 3).groupBy(identity).mapValues(_.map(_ => 1).reduce(_+_))
于 2019-01-20T20:16:39.817 回答
2

我不认为这真的更好,但你可以这样做:

List(1, 3, 4, 4, 2).groupBy(identity).maxBy(_._2.size)._1

不是最好的解决方案。您想要的是在列表上使用 maxBy 的某种方式,然后像这样引用列表:

val someList = List(1, 3, 4, 4, 2)
someList.maxBy(x => list.count(_ == x))
于 2012-12-14T12:11:51.773 回答
1

不,我认为这是最好的方法。但这不是很多工作......

list.groupBy(identity).mapValues(_.size)

给你

Map(2 -> 1, 4 -> 2, 1 -> 2, 3 -> 1)

然后,例如,您可以获取它.maxBy(_._2)(已编辑:感谢@Travis Brown!)并获取一个元组(4,2)(出现次数最多的数字以及出现次数)

如果你是单线的粉丝:

scala> List(1, 3, 4, 1, 4, 2).groupBy(identity).mapValues(_.size).maxBy(_._2)
res0: (Int, Int) = (4,2)
于 2012-12-14T12:07:53.573 回答
0

另一种变体:

val x = List(1, 3, 4, 1, 4, 2, 5, 5, 5)
 x.distinct.foldLeft((0,0))((a, b) => {
     val cnt = x.count(_ == b);
   if (cnt > a._1) (cnt, b) else a
 })._2
于 2012-12-14T12:15:20.217 回答