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::groupMapReduceこれは (その名前が示すように)groupBy後続mapValuesの削減ステップと同等です。
  • Map::maxByOptionmaxBy空のリストも簡単に処理する代わりに:
List(1, 3, 4, 4, 2, 3)
  .groupMapReduce(identity)(_ => 1)(_+_).maxByOption(_._2).map(_._1)
// Option[Int] = Some(4)

これ:

  • groups items (グループMapReduce のグループ部分)

  • mapグループ化された各値の出現を 1 にする (グループMap Reduce のマップ部分)

  • reduces 値のグループ内の値 ( _ + _) を合計する (groupMap Reduceの一部を減らす)。

  • 最後に、オカレンスの nbr ごとにオプションの最大値を取得し、それをマップして対応するアイテムを取得します。


リストが空ではないことがわかっている場合は、単純なmaxByものも機能します。

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

このパーツは、次の一連の1 つのパスで実行される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 に答える