Groovy List:Group By元素的计数并找到最高频率元素

时间:2015-06-05 19:38:03

标签: collections groovy

我有一个常规列表,如下所示

def certs = ['0xc1','0xc1','0xc1','0xc1','0xc2','0xc2','0xc3','0xc4','0xc4','0xc5','0xc5','0xc5','0xc5']

我试图通过计数找到每个元素和组的出现。 我试过了

certs.groupBy { it }.findAll { it.value.size() }

但我得到以下输出

[0xc1:[0xc1, 0xc1, 0xc1, 0xc1], 0xc2:[0xc2, 0xc2], 0xc3:[0xc3], 0xc4:[0xc4, 0xc4], 0xc5:[0xc5, 0xc5, 0xc5, 0xc5]]

相反,我希望低于

[0xc1:4, 0xc2:2, 0xc3:1, 0xc4:2, 0xc5:4]

有人可以帮我吗?另外,我想在列表中找到最大的元素0xc10xc5

更新

def myMap = certs.inject([:]) { m, x -> if (!m[x]) m[x] = 0; m[x] += 1; m }
def maxValue = myMap.values().max{it} 
def myKeys = []
myMap.findAll{ it.value == maxValue }.each{myKeys << it?.key}
println myKeys  // result = [0xc1:4, 0xc5:4]
//println myMap.sort { a, b -> b.value <=> a.value }

2 个答案:

答案 0 :(得分:11)

Map counts = certs.countBy { it }
counts.findAll { it.value == counts.values().max() }

或通过单行

certs.countBy { it }.groupBy { it.value }.max { it.key }.value.keySet()

答案 1 :(得分:5)

有几种方法可以做到这一点。开始学习Groovy集合方法的好地方是收集和注入。

方法collect为旧的集合生成一个新集合,采用一个闭包来描述如何更改现有集合的每个元素以获取新集合的新元素。

方法inject在给定集合的情况下生成新对象。它需要一个带有两个参数的闭包,一个用于运行的总对象,另一个用于当前集合的成员,其中闭包的主体显示如何修改集合的传入成员的运行总计。一个常见的例子是总结一个数字列表(虽然有一种方便的方法,总和,对于这种情况)。

所以你可以使用inject获取计数图:

m = certs.inject([:]) { m, x -> if (!m[x]) m[x] = 0; m[x] += 1; m }

这将为certs映射中的每个条目执行闭包,增加新映射中相同键的值,从而产生

[0xc1:4, 0xc2:2, 0xc3:1, 0xc4:2, 0xc5:4]
但是,这非常难看。闭包代码并不简单,我必须从闭包中返回映射,以便更新运行总计。

从groupBy开始生成地图,它不完全是您想要的地图。有一个类似于collect方法的方法,但专门用于地图,称为collectEntries,它允许您从一个集合或地图转换元素,从中生成新地图:

certs.groupBy().collectEntries { [(it.key) : it.value.size()] }

但是这两个都是不必要的,因为Groovy 1.8添加了一个更干净的countBy方法,看到this other answer更好的方法。

生成地图后,可以使用

查找值最大的条目
maxSize = m.values().max
m.entrySet().findAll { it.value == maxSize }