如何在不覆盖当前条目的情况下添加多个Groovy映射条目?

时间:2013-03-26 15:08:38

标签: map groovy

我对Groovy Maps的提问。我一直在寻找一种方法,以编程方式将新条目添加到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地图而不会覆盖地图中已有的值吗?

8 个答案:

答案 0 :(得分:10)

您需要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的multimap一起使用:

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")
}

答案 1 :(得分:9)

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

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()方法可以实现神奇。

答案 2 :(得分:5)

您也可以这样做:

// 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' ]

答案 3 :(得分:3)

如果你想在没有外部类的情况下进行多图处理,你可以只存储一个列表地图,语法不会很麻烦或者什么。

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 这个解决方案的优势在于它往往更加明显你正在做的事情......例如,它不是将单个项目转换为列表的魔法地图(这不是标准的地图合同),而是它&# 39; s始终是列表的地图,您只需将其用作列表地图。

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

答案 4 :(得分:2)

映射是一组键值映射,您可以按键插入不同的值,以便以后可以使用该键来查找它们。您的示例是一遍又一遍地插入相同键的值。您需要选择唯一的密钥。

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

class Stuff {
    String flagMnemonic
    String action
}

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

def editsMap = [:] 

如果您在此处使用了类型声明,并且如果flagId是String,则地图的类型将为Map<String, Stuff>

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

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 }

这样每当你从地图中要求某些东西时,你就会得到指定的默认对象。

答案 5 :(得分:1)

为什么不使用像:

这样的列表和闭包
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"

答案 6 :(得分:0)

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

您可以做的是将您的班级定义为

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'))

答案 7 :(得分:0)

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

就我而言,我从回购查询中得到了List<Map>;我需要类似Map<String, List<Object>>的东西,并且如果结果集的关键字与先前的关键字匹配,则需要添加到List中。当然,我的Object不是POJO,但是您可以使用任何Class。为了使它更复杂,我需要从一些结果值中创建一个组合键(不要问,我没有创建它),然后从原始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']
}