【问题标题】:elegant way to merge Map<String, List<String>> structure by using groovy使用 groovy 合并 Map<String, List<String>> 结构的优雅方法
【发布时间】:2020-10-09 11:35:08
【问题描述】:

我有一个嵌套地图结构:

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),然后合并案例列表。 即:

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 -&gt; v.collect{ c, l -&gt; }}的方式。有没有更好的解决方案? (即:使用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: []

【问题讨论】:

  • 对我来说最令人困惑的部分是,您正在嵌套 collectMany/collect ,然后在里面做一个副作用并丢弃结果。我会改用inject。类似于result = case_bool.findAll{,,,}.inject([:].withDefault...}) { r, kv -&gt; kv.values().each { c, l -&gt; r[c].addAll(l) }; r }
  • 感谢@cfrick。我试过你的方式println case_pool.findAll{ k, v -&gt; branch.contains(k) }.inject([:]) { r, kv -&gt; kv.values().each { c, l -&gt; r[c].addAll(l) }; r },并得到问题:No signature of method: java.util.LinkedHashMap$Entry.values() is applicable for argument types: () values: []

标签: groovy closures


【解决方案1】:

正如 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
}

【讨论】:

  • 这种方式确实比我第二次尝试的好。
【解决方案2】:

避免问题java.io.NotSerializableException: groovy.lang.MapWithDefaultin Jenkins,甚至失败by using @NonCPS。

以下是情侣解决方案:

  1. 继续使用@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 }
    
  2. 使用[].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()
        }
      }
    }
    
  3. 使用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()
      }
    }
    

【讨论】:

    猜你喜欢
    • 2020-10-21
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    • 2017-05-19
    • 2013-12-18
    • 1970-01-01
    • 2018-08-02
    • 2020-08-01
    相关资源
    最近更新 更多