【问题标题】:How do I add multiple Groovy map entries without overwriting the current entries?如何添加多个 Groovy 映射条目而不覆盖当前条目?
【发布时间】:2013-03-16 10:51:25
【问题描述】:

我对 Groovy 地图的问题。我一直在寻找一种以编程方式将新条目添加到 Groovy 映射而不覆盖当前条目的方法。例如

def editsMap = [:]

lineEdits.flag.each 
{ lineEdits_Flag ->
   editsMap.put('FlagId',lineEdits_Flag.id)
   editsMap.put('FlagMnemonic',lineEdits_Flag.mnemonic)
   editsMap.put('Action',lineEdits_Flag.action)   
   println "editsMap: ${editsMap}"
}

第一遍生成这张地图:
editsMap: [FlagId:10001, FlagMnemonic:TRA, Action:review]

但是第二遍会覆盖第一遍: editsMap: [FlagId:10002, FlagMnemonic:REB, Action:deny]

我要做的是在一张地图中创建多个条目。我需要我的地图来填充这样的内容:

editsMap: [FlagId:10001, FlagMnemonic:TRA, Action:review]
editsMap: [FlagId:10002, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:10003, FlagMnemonic:UNB, Action:deny]
editsMap: [FlagId:20001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:20002, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:30001, FlagMnemonic:REB, Action:deny]
editsMap: [FlagId:40001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:40002, FlagMnemonic:MPR, Action:review]
editsMap: [FlagId:50001, FlagMnemonic:CPT, Action:deny]
editsMap: [FlagId:60001, FlagMnemonic:DTU, Action:deny]
editsMap: [FlagId:70001, FlagMnemonic:ICD, Action:review]
editsMap: [FlagId:70002, FlagMnemonic:MPR, Action:review]

一旦我填充了我的地图,我就需要能够找到某些值来处理消息。我相信我可以使用类似的东西:

def thisValue = appliedEditsMap[FlagId, '10001'] ?: "default"

进行快速查找。

有人可以帮助我了解如何以编程方式将值添加到 Groovy 映射而不覆盖映射中已有的值吗?

【问题讨论】:

    标签: map groovy


    【解决方案1】:

    如果你想在没有外部类的情况下做多映射,你可以只存储一个列表映射,语法不会很麻烦或任何东西。

    def editsMap = [:].withDefault{[]}
    lineEdits.flag.each 
    { 
       lineEdits_Flag ->
       editsMap.FlagId << lineEdits_Flag.id
       editsMap.FlagMnemonic << lineEdits_Flag.mnemonic
       editsMap.Action << lineEdits_Flag.action
       println "editsMap: ${editsMap}"
    }
    

    或者如果你真的更喜欢你的原始语法,它看起来像: editsMap.get('FlagId').add(lineEdits_Flag.id) 甚至这应该工作: editsMap.get('FlagId') << lineEdits_Flag.id 这个解决方案的优点是它往往更明显你在做什么......例如,它不是将单个项目转换为列表的魔法地图(这不是标准地图合同),但它始终是列表的地图您只需将其用作列表的映射。

    .get 将始终以与描述多图相同的方式工作 - 它将始终返回地图中该项目的列表。

    【讨论】:

      【解决方案2】:

      我最近遇到了类似的问题,我知道这是可能的,因为一些同事已经这样做了。在这里阅读答案并进行实验,我终于找到了一个适合我的用例并且不难阅读的简单答案。编写一个“通用代码”答案会使它的可读性比在我们的代码中具有正确的列名等...

      就我而言,我从回购查询中得到了List&lt;Map&gt;;我需要Map&lt;String, List&lt;Object&gt;&gt; 之类的东西,如果结果集的键与前一个键匹配,我需要添加到List。当然,我的Object 不是 POJO,但你可以使用任何类。更复杂的是,我需要从一些结果值创建一个复合键(不要问,我没有创建它)从原始 Map 中删除这些键,所以我可以使用剩余的条目来创建业务对象。

      这就是我所做的:

      List<Map> listMap = repo.findWhateverItWas()
      
      Map<String, List<Object>> resultMap = [:].withDefault {[]}  //required to avoid NPE
      
      listMap.each { Map<String, Object> it ->
          String compKey = it['col1'] + it['col2'] + it['col3']
      
          Map tempMap =[:]
          it.keySet.each { k ->
              if (!(k in ['col1','col2','col3'])) {
                  tempMap << [(k): it[k]]        // this is the business Object result map
              }
          }
      
          // createEntity is a static Entity Factory
          // the simple += is the magic
          resultMap[(compKey)] += createEntity(Entity.class, tempMap)
      
      }
      
      return resultMap
      

      我意识到这并不能解决您的具体情况,但我相信它可以回答问题并为更复杂的情况提供答案。

      我能够通过一个简单的测试用例证明它的预期功能。我们使用 Spock...

      def "Map of Lists test"() {
          given:
              Map<String, List<String>> map = [:].withDefault { [] }
          when:
              map['key1'] += 'item1'
              map['key1'] += 'item2'
              map['key2'] += 'item3'
              map['key1'] += 'item4'
              map['key2'] += 'item5'
          then:
              map['key1'] == ['item1', 'item2', 'item4']
              map['key2'] == ['item3', 'item5']
      }
      

      【讨论】:

        【解决方案3】:

        为什么不使用列表和闭包:

        editsList = [
        [FlagId:10001, FlagMnemonic:TRA, Action:review],
        [FlagId:10002, FlagMnemonic:REB, Action:deny],
        [FlagId:10003, FlagMnemonic:UNB, Action:deny],
        [FlagId:20001, FlagMnemonic:REB, Action:deny],
        [FlagId:20002, FlagMnemonic:ICD, Action:review],
        [FlagId:30001, FlagMnemonic:REB, Action:deny],
        [FlagId:40001, FlagMnemonic:ICD, Action:review], 
        [FlagId:40002, FlagMnemonic:MPR, Action:review],
        [FlagId:50001, FlagMnemonic:CPT, Action:deny],
        [FlagId:60001, FlagMnemonic:DTU, Action:deny],
        [FlagId:70001, FlagMnemonic:ICD, Action:review],
        [FlagId:70002, FlagMnemonic:MPR, Action:review]
        ]
        def appliedEditsMap = {property,idValue->
             return editsList.find{it[property] == idValue}
        }
        def thisValue = appliedEditsMap(FlagId, '10001') ?: "default"
        

        【讨论】:

          【解决方案4】:

          几年前我在回答另一个网站上的类似问题时遇到了这个问题。我找不到它最初的来源,所以如果有人知道出处,请在此处发布。

          LinkedHashMap.metaClass.multiPut << { key, value ->
              delegate[key] = delegate[key] ?: []; delegate[key] += value
          }
          
          def myMap = [:]
          
          myMap.multiPut("a", "1")
          myMap.multiPut("a", "2")
          myMap.multiPut("a", "3")
          
          myMap.each {key, list ->
              println "${key} -> $value.list(",")
          }
          

          给予:

          a -> 1,2,3
          

          使用注入的 multiPut() 方法可以发挥作用。

          【讨论】:

            【解决方案5】:

            映射是一组键值映射,您可以通过键插入不同的值,以便以后可以使用键找到它们。您的示例是一遍又一遍地插入相同键的值。您需要选择唯一的键。

            创建一些类来存储地图中一个条目的值:

            class Stuff {
                String flagMnemonic
                String action
            }
            

            制作一张地图,您将使用 flagId 作为键(因为这是您唯一标识标志的方式)和 Stuff 作为值(因为它是您要查找的数据)。

            def editsMap = [:] 
            

            如果您在此处使用类型声明,并且如果 flagId 是字符串,则映射的类型将为 Map&lt;String, Stuff&gt;

            现在你可以在地图上放东西了:

            lineEdits.flag.each { lineEdits_Flag -> 
                editsMap[lineEdits_Flag.id] = 
                new Stuff(
                    flagMnemonic: lineEdits_Flag.mnemonic, 
                    action: lineEdits_Flag.action) 
            }
            

            然后用

            把它取出来
            def myStuffFor10001 = editsMap['10001']
            println myStuffFor10001.flagMnemonic // should equal 'TRA'
            println myStuffFor10001.action // should equal 'review'
            

            除了使用?: "default" 设置默认值之外,还有一个简单的替代方法,您可以在创建地图时使用withDefault

            def defaultStuff = new Stuff(
                flagMnemonic: "defaultMnemonic", action:"defaultAction")
            def editsMap = [:].withDefault { defaultStuff }
            

            因此,每当您从地图中请求不存在的东西时,您都会获得指定的默认对象。

            【讨论】:

              【解决方案6】:

              你想要像Guava's MultiMap这样的东西:

              Multimap<String, String> myMultimap = ArrayListMultimap.create();
              
              // Adding some key/value
              myMultimap.put("Fruits", "Bannana");
              myMultimap.put("Fruits", "Apple");
              myMultimap.put("Fruits", "Pear");
              myMultimap.put("Vegetables", "Carrot");
              
              // Getting values
              Collection<string> fruits = myMultimap.get("Fruits");
              System.out.println(fruits); // [Bannana, Apple, Pear]
              

              This guy 对 Multimap 进行了纯 Groovy 模拟:

              class GroovyMultimap {
                  Map map = [:]
              
                  public boolean put(Object key, Object value) {
                      List list = map.get(key, [])
                      list.add(value)
                      map."$key" = list
                  }
              }
              

              您可以在映射操作中使用putAtgetAt 作为语法糖。您也可以在地图对象中尝试mixin

              他还将 Groovy 与 Guava 的多图一起使用:

              List properties = ['value1', 'value2', 'value3']
              Multimap multimap = list.inject(LinkedListMultimap.create()) {
                  Multimap map, object ->
                  properties.each {
                      map.put(it, object."$it")
                  }
                  map
              }
              properties.each {
                  assertEquals (multimap.get(it), list."$it")
              }
              

              【讨论】:

              • 你也可以这样做:[:].withDefault { [] }.with { map -&gt; map.fruit &lt;&lt; 'banana' ; map.fruit &lt;&lt; 'apple' ; map.veg &lt;&lt; 'carrot' ; map }
              【解决方案7】:

              你也可以这样做:

              // Dummy map for testing
              lineEdits = [ flag:[
                [id:10001, mnemonic:'TRA', action:'review'],
                [id:10002, mnemonic:'REB', action:'deny'],
                [id:10003, mnemonic:'UNB', action:'deny'],
                [id:20001, mnemonic:'REB', action:'deny'],
                [id:20002, mnemonic:'ICD', action:'review'],
                [id:30001, mnemonic:'REB', action:'deny'],
                [id:40001, mnemonic:'ICD', action:'review'],
                [id:40002, mnemonic:'MPR', action:'review'],
                [id:50001, mnemonic:'CPT', action:'deny'],
                [id:60001, mnemonic:'DTU', action:'deny'],
                [id:70001, mnemonic:'ICD', action:'review'],
                [id:70002, mnemonic:'MPR', action:'review'] ] ]
              
              def editsMap = lineEdits.flag
                                      .groupBy { it.id } // Group by id
                                      .collectEntries { k, v ->
                                        [ k, v[ 0 ] ] // Just grab the first one (flatten)
                                      }
              
              assert editsMap[ 60001 ] == [ id:60001, mnemonic:'DTU', action:'deny' ]
              

              【讨论】:

                【解决方案8】:

                您需要将其放入一个类中,然后将该类添加到地图中。从我看到你的信息是相关的,所以有一个类来存储是有意义的,除非我错过任何东西

                你可以做的是将你的类定义为

                class Flag {
                
                String flagID
                String flagMnemonic
                String action
                }
                
                Now Put your Flag in to your map as
                
                editsMap.put(10000,newFlag(flagID:'10000',flagMnemonic:'TES',action:'tes'))
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2015-08-10
                  • 1970-01-01
                  • 1970-01-01
                  • 2016-04-25
                  • 2011-06-20
                  相关资源
                  最近更新 更多