使用 groovy 合并 Map<String, List<String>> 结构的优雅方式
elegant way to merge Map<String, List<String>> structure by using groovy
我有一个嵌套映射结构:
Map<String, List<String>> case_pool = [
dev : [
funcA : ['devCaseA'] ,
funcB : ['devCaseB'] ,
funcC : ['devCaseC']
],
'dev/funcA' : [
funcA : ['performanceCaseA']
],
'dev/funcA/feature' : [
funcA : ['performanceCaseA', 'featureCase']
],
staging : [
funcB : ['stgCaseB'] ,
funcC : ['stgCaseC']
]
]
想要得到结果,当branch.contains(case_pool.key)
,然后合并cases列表。
即:
String branch = 'dev/funcA/feature-1.0'
// will final get result of " 'dev' + 'dev/funcA' + 'dev/funcA/feature' ":
result:
[
funcA: [ "devCaseA", "performanceCaseA", "featureCase" ],
funcB: [ "devCaseB" ],
funcC: [ "devCaseC" ]
]
首先我使用了循环:
String branch = 'dev/funcA/feature-1.0'
def result = [:].withDefault { [] as Set }
case_pool.keySet().each {
if ( branch.contains(it) ) {
case_pool.get(it).each { k, v ->
result[k].addAll(v)
}
}
}
println 'result: ' + result
其次,我正在使用闭包:
String branch = 'dev/funcA/feature-1.0'
def result = [:].withDefault { [] as Set }
case_pool.findAll{ k, v -> branch.contains(k) }.collectMany{ k, v -> v.collect{ c, l ->
result[c].addAll(l)
}}
println 'result: ' + result
不过,我不喜欢.collectMany{ k, v -> v.collect{ c, l -> }}
的方式。有没有更好的解决方案? (即:使用 groupBy
或其他)
顺便说一句,我已经尝试了 collectEntries
,结果最终列表将替换所有:
String branch = 'dev/funcA/feature-1.0'
println case_pool.findAll{ k, v -> branch.contains(k) }.collect{ k, v -> v}.collectEntries{it}
result: [funcA:[performanceCaseA, featureCase], funcB:[devCaseB], funcC:[devCaseC]]
最后的funcA : ['performanceCaseA', 'featureCase']
替换了全部funcA: []
正如 cfrick 所说,inject
在这里更好,即:
def result = case_pool.inject([:].withDefault { [] as Set }) { result, key, value ->
if (branch.contains(key)) {
value.each { k, v ->
result[k] += v
}
}
result
}
避免问题 java.io.NotSerializableException: groovy.lang.MapWithDefault
in Jenkins, and even failed by using @NonCPS.
以下是情侣解决方案:
- 继续使用@tim_yates发布的
inject([:].withDefault{})
,只需将groovy.lang.MapWithDefault
重新分配给java.util.LinkedHashMap
:
Map<String, List<String>> result = [:]
case_pool.inject([:].withDefault { [] as Set }) { res, key, value ->
if (branch.contains(key)) {
value.each { k, v -> res[k] += v }
}; result
}.collect { k, v -> result[k] = v }
- 使用
[].flatten().unique()
:
Map<String, List<String>> result = [:]
case_pool.collect { key, value ->
if (branch.contains(key)) {
value.collect { k, v ->
result[k] = [result.getOrDefault(k,[]), v].flatten().unique()
}
}
}
- 使用
findAll{}
+ collect{}
:
Map<String, List<String>> result = [:]
case_pool.findAll{ k, v -> branch.contains(k) }.collect{ k, v ->
v.keySet().each {
result[it] = (result.getOrDefault(it,[]) + v[it]).flatten().unique()
}
}
我有一个嵌套映射结构:
Map<String, List<String>> case_pool = [
dev : [
funcA : ['devCaseA'] ,
funcB : ['devCaseB'] ,
funcC : ['devCaseC']
],
'dev/funcA' : [
funcA : ['performanceCaseA']
],
'dev/funcA/feature' : [
funcA : ['performanceCaseA', 'featureCase']
],
staging : [
funcB : ['stgCaseB'] ,
funcC : ['stgCaseC']
]
]
想要得到结果,当branch.contains(case_pool.key)
,然后合并cases列表。
即:
String branch = 'dev/funcA/feature-1.0'
// will final get result of " 'dev' + 'dev/funcA' + 'dev/funcA/feature' ":
result:
[
funcA: [ "devCaseA", "performanceCaseA", "featureCase" ],
funcB: [ "devCaseB" ],
funcC: [ "devCaseC" ]
]
首先我使用了循环:
String branch = 'dev/funcA/feature-1.0'
def result = [:].withDefault { [] as Set }
case_pool.keySet().each {
if ( branch.contains(it) ) {
case_pool.get(it).each { k, v ->
result[k].addAll(v)
}
}
}
println 'result: ' + result
其次,我正在使用闭包:
String branch = 'dev/funcA/feature-1.0'
def result = [:].withDefault { [] as Set }
case_pool.findAll{ k, v -> branch.contains(k) }.collectMany{ k, v -> v.collect{ c, l ->
result[c].addAll(l)
}}
println 'result: ' + result
不过,我不喜欢.collectMany{ k, v -> v.collect{ c, l -> }}
的方式。有没有更好的解决方案? (即:使用 groupBy
或其他)
顺便说一句,我已经尝试了 collectEntries
,结果最终列表将替换所有:
String branch = 'dev/funcA/feature-1.0'
println case_pool.findAll{ k, v -> branch.contains(k) }.collect{ k, v -> v}.collectEntries{it}
result: [funcA:[performanceCaseA, featureCase], funcB:[devCaseB], funcC:[devCaseC]]
最后的funcA : ['performanceCaseA', 'featureCase']
替换了全部funcA: []
正如 cfrick 所说,inject
在这里更好,即:
def result = case_pool.inject([:].withDefault { [] as Set }) { result, key, value ->
if (branch.contains(key)) {
value.each { k, v ->
result[k] += v
}
}
result
}
避免问题 java.io.NotSerializableException: groovy.lang.MapWithDefault
in Jenkins, and even failed by using @NonCPS.
以下是情侣解决方案:
- 继续使用@tim_yates发布的
inject([:].withDefault{})
,只需将groovy.lang.MapWithDefault
重新分配给java.util.LinkedHashMap
:Map<String, List<String>> result = [:] case_pool.inject([:].withDefault { [] as Set }) { res, key, value -> if (branch.contains(key)) { value.each { k, v -> res[k] += v } }; result }.collect { k, v -> result[k] = v }
- 使用
[].flatten().unique()
:Map<String, List<String>> result = [:] case_pool.collect { key, value -> if (branch.contains(key)) { value.collect { k, v -> result[k] = [result.getOrDefault(k,[]), v].flatten().unique() } } }
- 使用
findAll{}
+collect{}
:Map<String, List<String>> result = [:] case_pool.findAll{ k, v -> branch.contains(k) }.collect{ k, v -> v.keySet().each { result[it] = (result.getOrDefault(it,[]) + v[it]).flatten().unique() } }