2012-12-14 126 views
10

在集合中找到最常見/最常見的元素的最佳方式是什麼?例如:找到一個集合中最常見/最常見的元素?

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

嗯。什麼人可以做的是做一個groupBy第一,然後通過lengthmap它們,然後選擇最大的一個。這樣的話,你會得到:

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 

然後在最後,選擇映射到最高數字(2)的關鍵(4)。 (嵌套問題:這樣做的最佳方法是什麼?)。但是,對於這樣一個簡單的操作來說,這似乎有很多工作。

有沒有更好/更習慣的方式來做到這一點?

+3

嵌套答案:使用'maxBy'。 – senia

+0

請記住,可能存在多個最大值,在這種情況下,您可以按照找到的最大值過濾您的地圖。 –

回答

20

我不得不說:

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 

這顯然比上面的單行可讀要少得多,雖然,所以我建議你與他們堅持,除非你能證明(即標杆,不猜測)他們是你的應用程序的瓶頸。

+0

在'maxBy'之前你不需要'toSeq'。 – senia

+0

@senia。啊,當然。編輯。 –

1

不,我認爲這是最好的方法。但它不是大量的工作......

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

給你

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

然後,例如,你可以把它.maxBy(_._2)(編輯:!感謝@Travis布朗),並得到一個元組(4,2)如果你是一個俏皮話風扇

(出現次數最多的,它出現的次數數):

scala> List(1, 3, 4, 1, 4, 2).groupBy(identity).mapValues(_.size).maxBy(_._2) 
res0: (Int, Int) = (4,2) 
+0

你的單線只在這裏工作,因爲'4'也是最大的價值。你需要'maxBy'(看我的答案)。 –

+0

@TravisBrown:你說得對,謝謝! –

+0

您提供的單行代碼顯示了此方法的缺陷。 1和4都是最常見的值,但只給出了4。 –

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)) 
+2

我是一個在大多數情況下選擇可讀性而不是性能提升的倡導者,但我不確定二次問題的二次方案是否是一個好主意。 –

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