web-dev-qa-db-fra.com

Comment ajouter plusieurs entrées de carte Groovy sans écraser les entrées actuelles?

Ma question avec Groovy Maps. J'ai cherché un moyen d'ajouter par programme une nouvelle entrée à une carte Groovy sans écraser l'entrée actuelle. Par exemple

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

Le premier passage produit cette carte:
editsMap: [FlagId: 10001, FlagMnemonic: TRA, Action: examen]

Mais le second passage écrase le premier avec: editsMap: [FlagId: 10002, FlagMnemonic: REB, Action: deny]

Ce que j'essaie de faire est de créer plusieurs entrées dans la même carte. J'ai besoin que ma carte remplisse quelque chose comme ceci:

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]

Une fois ma carte renseignée, je dois pouvoir trouver certaines valeurs pour traiter un message. Je crois que je peux utiliser quelque chose comme:

def thisValue = appliedEditsMap[FlagId, '10001'] ?: "default"

faire une recherche rapide.

Quelqu'un peut-il m'aider à comprendre comment ajouter par programme des valeurs à une carte Groovy sans écraser les valeurs déjà présentes dans la carte?

10
kbhuffaker

Vous voulez quelque chose comme Multi Map Guava :

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]

Ce gars fait une pure émulation Groovy de Multimap:

class GroovyMultimap {
    Map map = [:]

    public boolean put(Object key, Object value) {
        List list = map.get(key, [])
        list.add(value)
        map."$key" = list
    }
}

Vous pouvez utiliser putAt et getAt pour le sucre syntaxique dans les opérations de carte. Vous pouvez également essayer un mixin dans un objet de la carte.

Il utilise également Groovy avec la carte multiple de 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")
}
10
Will

J'ai découvert cela il y a plusieurs années en réponse à une question similaire sur un autre site. Je ne trouve pas d'où il vient à l'origine, donc si quelqu'un connaît la source, merci de l'afficher ici.

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

Donne:

a -> 1,2,3

L'utilisation de la méthode multiPut () injectée fait la magie.

9
rifboy

Vous pouvez aussi faire quelque chose comme ça:

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

Si vous voulez utiliser plusieurs cartes sans classes externes, vous pouvez simplement stocker une carte de listes, la syntaxe ne sera pas fastidieuse.

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}" } ou si vous préfériez vraiment votre syntaxe d'origine, elle ressemblerait à ceci: editsMap.get('FlagId').add(lineEdits_Flag.id) ou même cela devrait fonctionner: editsMap.get('FlagId') << lineEdits_Flag.id L'avantage de cette solution est qu'elle a tendance à être plus évidente dans ce que vous faites ... convertit des éléments uniques en liste (ce qui n’est pas le contrat de carte standard), mais c’est toujours une carte de listes que vous utilisez simplement comme une carte de listes.

Le .get fonctionnera toujours de la même manière que la carte multiple a été décrite: il retournera toujours la liste de cet élément sur la carte.

3
Bill K

Une carte est un ensemble de mappages clé-valeur, vous insérez différentes valeurs par clé afin que vous puissiez utiliser la clé pour les retrouver ultérieurement. Votre exemple consiste à brancher des valeurs pour les mêmes clés à plusieurs reprises. Vous devez choisir des clés uniques.

Faites de la classe pour stocker vos valeurs pour une entrée dans la carte:

class Stuff {
    String flagMnemonic
    String action
}

Créez une carte où vous utiliserez flagId comme clé (car c'est ainsi que vous identifiez l'indicateur de manière unique) et Stuff comme valeur (car ce sont les données que vous souhaitez rechercher).

def editsMap = [:] 

Si vous avez utilisé des déclarations de type ici et si flagId est une chaîne, le type de la carte serait Map<String, Stuff>.

Maintenant, vous pouvez mettre des choses dans la carte:

lineEdits.flag.each { lineEdits_Flag -> 
    editsMap[lineEdits_Flag.id] = 
    new Stuff(
        flagMnemonic: lineEdits_Flag.mnemonic, 
        action: lineEdits_Flag.action) 
}

et le sortir avec

def myStuffFor10001 = editsMap['10001']
println myStuffFor10001.flagMnemonic // should equal 'TRA'
println myStuffFor10001.action // should equal 'review'

Il existe également une alternative simple à l'utilisation de ?: "default" pour définir les valeurs par défaut. Vous pouvez utiliser withDefault lors de la création de votre carte:

def defaultStuff = new Stuff(
    flagMnemonic: "defaultMnemonic", action:"defaultAction")
def editsMap = [:].withDefault { defaultStuff }

de sorte que chaque fois que vous demandez quelque chose de la carte qui n'y est pas présent, vous obtenez l'objet par défaut spécifié.

Vous devez mettre cela dans une classe, puis l'ajouter à la carte. D'après ce que je vois, vos informations sont liées. Il est donc logique d'avoir un cours à stocker sauf si je manque quelque chose

Ce que vous pouvez faire est de définir votre classe comme

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

Pourquoi ne pas utiliser une liste et une clôture comme:

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"
0
Dave