问题:
如何合并地图,同时总结地图中公共密钥的值。
输入:
[a: 10, b:2, c:3]
[b:3, c:2, d:5]
输出
[a:10, b:5, c:5, d:5]
扩展问题:
如何通过对2个映射中的公共键的值应用函数(Closure)来合并原始的2个映射。即..而不是简单地总结公共密钥的值,让用户指定要使用的功能。
例如:如果用户想要使用' min'函数而不是求和,然后可以指定min来得到[a:10, b:2, c:2, d:5]
作为结果。
答案 0 :(得分:4)
当key的映射值为null时,可以使用inject with?:
map1 = [a:10, b:2, c:3]
map2 = [b:3, c:2, d:5]
(map1.keySet() + map2.keySet())
.inject([:]) {m, k -> m[k] = (map1[k] ?: 0) + (map2[k] ?: 0); m }
评估为
[a:10, b:5, c:5, d:5]
或者你可以使用collectEntries(这样的闭包不是那么难看):
map1 = [a:10, b:2, c:3]
map2 = [b:3, c:2, d:5]
(map1.keySet() + map2.keySet())
.collectEntries {[(it) : (map1[it] ?: 0) + (map2[it] ?: 0)]}
为了使这个通用,允许传入一个闭包。但是,collectEntries已经允许这样做,你不会获得太多收益。
答案 1 :(得分:3)
在groovy脚本下面使用并使用闭包来解决OP问题。这将有助于决定用户为合并地图中每个键的值选择merge strategy
。
注意:脚本示例使用3个映射来确保脚本能够处理多个映射的合并。 即使要处理更多地图,此处提供的此解决方案也会扩展。
在合并时,每个地图可能没有所有键,因此当用户尝试获取值时,可能会有null
。因此,从传递给null
的列表中删除Collection
。
/**
* this script to merge the maps based on the closure provided by user based on different use case
*/
//For sample, taking below 3 maps
def map1 = [a:10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def map3 = [d:3,a:4,e:9]
//Below method takes list of maps and closure as input and returns merged map
def getMergedMap(list, closure) {
def keys = [] as Set
list.each { element -> keys.addAll(element.keySet()) }
def map = [:]
keys.each { k ->
def items = []
list.each { items.add(it[k]) }
map[k] = closure(items)
}
map
}
//Create the list of maps
def mapList = [map1, map2, map3]
//Call the above method and pass the closure are need for merging condition, here min of matched key values from multiple maps
def newmap = getMergedMap(mapList) { list -> Collections.min(list - null) }
println newmap
//Call the above method and pass the closure are need for merging condition, here max of matched key values from multiple maps
newmap = getMergedMap(mapList) { list -> Collections.max(list - null) }
println newmap
//Call the above method and pass the closure are need for merging condition, here sum of matched key values from multiple maps
newmap = getMergedMap(mapList) { list -> (list-null).sum() }
println newmap
上述代码的输出:
[a:4, b:2, c:2, d:3, e:9]
[a:10, b:3, c:3, d:5, e:9]
[a:14, b:5, c:5, d:8, e:9]
UPDATE:如果您想要合并时的默认行为,请按合并顺序保留上一个映射的值,可以使用下面的闭包调用
newmap = getMergedMap(mapList) { list -> (list-null).last() }
println newmap
结果:
[a:4, b:3, c:2, d:3, e:9]
您可以从此处快速测试脚本 Demo
<强> UPDATE2:强>
上述getMeredMap
简单易读。当然,可以使用多个inject
进行groovified /浓缩,如下图所示:
def getNewMap(list, closure) {
list.inject([], { klist, map -> klist.addAll(map.keySet()); klist as Set }).inject([:]) { m, k -> m[k] = closure(list.inject([]){ vlist,map -> vlist << map[k] });m }
}
更新3
您还可以通过为合并值策略单独定义闭包来简化调用代码。 imo,这简化了一点。在内部合并时也处理空值而不是让用户在外面处理,这对于那些使用getMergedMap
方法的人来说会更加干净。
//Merging of multiple maps with different merge strategies
//And handled null inside of mergeMethod instead of outside like earlier
def map1 = [a:10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def map3 = [d:3, a:4, e:9]
//Input map list and Merge strategy closure and handling null
def getMergedMap(list, closure) {
list.inject([],{ klist, map -> klist.addAll(map.keySet());klist as Set}).inject([:]) { m, k -> m[k] = closure(list.inject([]){ vlist,map -> vlist << map[k];vlist-null });m }
}
def mapList = [map1, map2, map3]
//Closures for merged value strategy
def minValue = { list -> Collections.min(list) }
def maxValue = { list -> Collections.max(list) }
def totalValue = { list -> list.sum() }
def defaultValue = { list -> list.last() }
//Call merge maps with strategies and assert
assert [a:4, b:2, c:2, d:3, e:9] == getMergedMap(mapList, minValue)
assert [a:10, b:3, c:3, d:5, e:9] == getMergedMap(mapList, maxValue)
assert [a:14, b:5, c:5, d:8, e:9] == getMergedMap(mapList, totalValue)
assert [a:4, b:3, c:2, d:3, e:9] == getMergedMap(mapList, defaultValue)
答案 2 :(得分:2)
这样就够了吗?
Map one = [a:10, b:2, c:3]
Map two = [b:3, c:2, d:5]
Map mergeOn(Map one, Map two, Closure closure) {
two.inject([:] << one) { acc, key, val ->
key in acc.keySet() ? acc[key] = closure(acc[key], val) : acc << [(key): val]
acc
}
}
assert mergeOn(one, two) { a, b -> a + b } == [a:10, b:5, c:5, d:5]
assert mergeOn(one, two) { a, b -> a - b } == [a:10, b:-1, c:1, d:5]
assert mergeOn(one, two) { a, b -> a * b } == [a:10, b:6, c:6, d:5]
assert mergeOn(one, two) { a, b -> Math.max(a, b) } == [a:10, b:3, c:3, d:5]
assert mergeOn(one, two) { a, b -> Math.min(a, b) } == [a:10, b:2, c:2, d:5]
答案 3 :(得分:0)
第一个可以通过以下方式完成:
/* Transform entries in map z by adding values of keys also present in zz
* Take any entries in map zz whose keys are not in z. Add the result.
*/
Map mergeMaps(Map z, Map zz){
Map y = z.inject([:]) { result, e -> zz.keySet().contains(e.key) ? result << [(e.key) : e.value + zz[e.key]] : result << e }
Map yy = zz.findAll { e -> !z.keySet().contains(e.key) }
y + yy
}
现在让我们在Groovy控制台上使用它:
mergeMaps([a: 10, b:2, c:3], [b:3, c:2, d:5])
Result: [a:10, b:5, c:5, d:5]
扩展的问题(更通用)可以通过一个小的调整来完成:
Map mergeMapsWith(Map z, Map zz, Closure cls){
Map y = z.inject([:]) { result, e -> zz.keySet().contains(e.key) ? result << [(e.key) : cls.call(e.value,zz[e.key])] : result << e }
Map yy = zz.findAll { e -> !z.keySet().contains(e.key) }
y + yy
}
现在让我们在Groovy控制台上使用它:
mergeMapsWith([a: 10, b:2, c:3], [b:3, c:2, d:5]) { a, b -> Math.min(a,b)}
Result: [a:10, b:2, c:2, d:5]
或者如果我们想要与乘法合并:
mergeMapsWith([a: 10, b:2, c:3], [b:3, c:2, d:5]) { a, b -> a * b }
Result: [a:10, b:6, c:6, d:5]
答案 4 :(得分:0)
这是一个简单的解决方案,它收集唯一键,将每个键的值作为数组收集,并将lambda应用于每个键的值数组。在第一个中,lambda接受一个数组:
def process(def myMaps, Closure myLambda) {
return myMaps.sum { it.keySet() }.collectEntries { key ->
[key, myLambda(myMaps.findResults { it[key] })]
}
}
def map1 = [a: 10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def maps = [map1, map2]
def sumResult = process(maps) { x -> x.sum() }
def prodResult = process(maps) { x -> x.inject(1) { a, b -> a * b } }
def minResult = process(maps) { x -> x.inject(x[0]) { a, b -> a < b ? a : b } }
assert sumResult == [a:10, b:5, c:5, d:5]
assert prodResult == [a:10, b:6, c:6, d:5]
assert minResult == [a:10, b:2, c:2, d:5]
在第二版中,lambda表达式采用两个值:
def process(def myMaps, Closure myLambda) {
return myMaps.sum { it.keySet() }.collectEntries { key ->
[key, { x ->
x.subList(1, x.size()).inject(x[0], myLambda)
}(myMaps.findResults { it[key] })]
}
}
def map1 = [a: 10, b:2, c:3]
def map2 = [b:3, c:2, d:5]
def maps = [map1, map2]
def sumResult = process(maps) { a, b -> a + b }
def prodResult = process(maps) { a, b -> a * b }
def minResult = process(maps) { a, b -> a < b ? a : b }
assert sumResult == [a:10, b:5, c:5, d:5]
assert prodResult == [a:10, b:6, c:6, d:5]
assert minResult == [a:10, b:2, c:2, d:5]