我对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地图而不会覆盖地图中已有的值吗?
答案 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
}
}
您可以在地图操作中使用putAt
和getAt
来获取合成糖。您还可以在地图对象中尝试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']
}