Как добавить несколько записей карты 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'))