Как добавить несколько записей карты Groovy без перезаписи текущих записей?
Мой вопрос с 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, Действие: обзор]
Но второй проход перезаписывает первый проход: editsMap: [FlagId: 10002, FlagMnemonic: REB, Действие: 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, не перезаписывая значения, уже существующие на карте?
Ответы
Ответ 1
Вы хотите что-то вроде 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
}
}
Вы можете использовать putAt
и getAt
для синтаксического сахара в операциях с картами. Вы также можете попробовать 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")
}
Ответ 2
Я столкнулся с этим несколько лет назад как ответ на аналогичный вопрос на другом сайте. Я не могу найти, откуда он был изначально, поэтому, если кто-нибудь знает источник, отправьте его здесь.
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() делает магию.
Ответ 3
Вы также можете сделать что-то вроде этого:
// 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' ]
Ответ 4
Карта представляет собой набор отображений значений ключа, вы подключаете разные значения по ключу, чтобы вы могли использовать ключ, чтобы найти их позже. Ваш пример снова и снова включает значения для одних и тех же клавиш. Вам нужно выбрать уникальные ключи.
Сделайте некоторый класс для хранения ваших значений для одной записи на карте:
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 }
так что всякий раз, когда вы запрашиваете что-то с карты, которая там отсутствует, вы получаете указанный объект по умолчанию.
Ответ 5
Если вы хотите сделать многомачтовую вещь без внешних классов, вы можете просто сохранить карту списков вместо этого, синтаксис не будет громоздким или что-то еще.
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 всегда будет работать так же, как описано в мультимаге - он всегда будет возвращать список для этого элемента на карте.
Ответ 6
Вам нужно поместить это в класс, а затем добавить этот класс в 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'))