Finding the most frequent/common element in a collection?
I have to say that:
list.groupBy(identity).mapValues(_.size).maxBy(_._2)._1
Or just:
list.groupBy(identity).maxBy(_._2.size)._1
Doesn't really seem like that much work to me.
If you're worried about the overhead of building up the lists for each value when you only need counts, you could do the following:
list.foldLeft(Map.empty[Int, Int].withDefaultValue(0)) {
case (m, v) => m.updated(v, m(v) + 1)
}.maxBy(_._2)._1
Or even keep track of the maximum as you go, to avoid the extra traversal at the end:
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
This is obviously much less readable than the one-liners above, though, so I'd recommend sticking with them unless you can show (i.e., with benchmarking, not speculation) that they're a bottleneck in your application.
Starting in Scala 2.13
, we can use:
List::groupMapReduce
which is (as its name suggests) an equivalent of agroupBy
followed bymapValues
and a reduce step.Map::maxByOption
instead of a simplemaxBy
to also handle empty lists:
List(1, 3, 4, 4, 2, 3)
.groupMapReduce(identity)(_ => 1)(_+_).maxByOption(_._2).map(_._1)
// Option[Int] = Some(4)
This:
group
s items (group part of groupMapReduce)map
s each grouped value occurrence to 1 (map part of groupMapReduce)reduce
s values within a group of values (_ + _
) by summing them (reduce part of groupMapReduce).finally gets the optional maximum by nbr of occurrences and map it to get the corresponding item.
If you know your list is not empty, then a simple maxBy
also works:
List(1, 3, 4, 4, 2, 3).groupMapReduce(identity)(_ => 1)(_+_).maxBy(_._2)._1
// 4
The groupMapReduce
part is an equivalent version performed in one pass through the sequence of:
List(1, 3, 4, 4, 2, 3).groupBy(identity).mapValues(_.map(_ => 1).reduce(_+_))