17

我对 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 地图而不覆盖地图中已有的值吗?

4

8 回答 8

12

你想要Guava 的 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]

这家伙对 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")
}
于 2013-03-26T15:56:35.753 回答
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()方法的使用具有魔力。

于 2013-10-23T18:19:25.100 回答
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' ]
于 2013-03-26T15:45:06.340 回答
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 这个解决方案的优点是它往往更明显你在做什么......例如,它不是转换单个项目的魔法地图到一个列表(这不是标准的地图合同),但它始终是一个列表的映射,您只需将其用作列表的映射。

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

于 2017-06-09T20:52:32.063 回答
2

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

制作一些类来存储地图中一个条目的值:

class Stuff {
    String flagMnemonic
    String action
}

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

def editsMap = [:] 

如果您在此处使用类型声明,并且如果 flagId 是字符串,则地图的类型将为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 }

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

于 2013-03-26T15:20:39.377 回答
2

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

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"
于 2018-04-05T14:06:22.023 回答
0

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

您可以做的是将您的课程定义为

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'))
于 2013-03-26T15:15:59.363 回答
0

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

List<Map>就我而言,我从回购查询中得到了回复;我需要类似的东西,如果结果集的键与前一个键匹配Map<String, List<Object>>,我需要添加。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

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

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

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']
}
于 2019-11-22T15:10:50.660 回答