如何使用gpars和map reduce折叠成多级地图

时间:2013-05-07 17:09:44

标签: groovy gpars

我有一组看起来像这样的地图:

def list = [
    [key1: 'ABC', key2: 3, value: 1.01],
    [key1: 'ABC', key2: 4, value: 1.02],
    [key1: 'ABC', key2: 4, value: 1.03],
    [key1: 'DEF', key2: 3, value: 1.04]]

我正在尝试获得一个看起来像这样的结果,该结果对唯一的key1和key2值的值进行分组和汇总,并产生层次结构。

['ABC':[[key2: 2, value: 1.01]
        [key2: 4, value: 2.05]],   //note values are added
 'DEF':[[key2: 3, value: 1.04]]
]

有许多具有一个键的映射例程的示例,但在使用多个键时折叠这些键的最佳方法是什么?

我想到的一个解决方案是使用groupby来获取按第一个键分组的列表。那里的问题是必须在每个元素的子列表上运行combine或reduce:

list.parallel
.map{it}
.groupBy{it.key1}

此时我想减少分组地图的.value(),这是我在链中无法真正做到的

我也尝试使用combine,它有点像示例here。然而,看起来如果联合收回地图,它想要进一步合并。

def result = list.parallel
        .map{[it.key1, it]}
        .combine({-> [:]})  { map, v -> println "$map - $v = ${v.getClass()}"
            map[v.key2] = map[v.key2]?:0 + v.value
            map
        }

然后可以选择减少地图,但是减少例程变成了一个非常复杂的组合嵌套地图的野兽。所以我想知道是否有更简单的东西,或者我应该只运行一个reduce例程来组合复杂的地图。

list.parallel
.map{[(it.key1):it]}
.reduce([:]) { a, b ->
   complexMapCombiner(a, b)
}

1 个答案:

答案 0 :(得分:2)

所以这是一个有效的解决方案,但不如我想要的优雅。如果有人有更好的东西请发表答案。

@Grab(group='org.codehaus.gpars', module='gpars', version='1.0.0')

import static groovyx.gpars.GParsPool.*

def list = [
    [key1: 'ABC', key2: 3, value: 1.01],
    [key1: 'ABC', key2: 4, value: 1.02],
    [key1: 'ABC', key2: 4, value: 1.03],
    [key1: 'DEF', key2: 3, value: 1.04]]

withPool {
    def mapInner = { entrylist ->
         withPool{
             entrylist.getParallel()
                 .map{[it.key2, it.value]}
                 .combine(0) {acc, v -> acc + v}.getParallel()
                 .map{[key2: it.key, value: it.value]}.collection
         }
    }

    //for dealing with bug when only 1 list item
    def collectSingle = { entrylist ->
        def first = entrylist[0]
        return [[key2:(first.key2), value:first.value]]
    }

    def result = list.parallel
        .groupBy{it.key1}.getParallel()
        .map{ [(it.key) : (it.value?.size())>1?mapInner.call(it.value):collectSingle.call(it.value) ] }
        .reduce([:]) {a, b -> a + b}


    println "result = $result"
}