Mappe in Groovy

1. Panoramica

Groovy estende l' API Map in Java per fornire metodi per operazioni come il filtro, la ricerca e l'ordinamento . Fornisce inoltre una varietà di modi abbreviati per creare e manipolare mappe.

In questo articolo vedremo il modo Groovy di lavorare con le mappe.

2. Creazione di Groovy Mappa s

Possiamo usare la sintassi letterale della mappa [k: v] per creare mappe. Fondamentalmente, ci consente di istanziare una mappa e definire le voci in una riga.

È possibile creare una mappa vuota utilizzando:

def emptyMap = [:]

Allo stesso modo, una mappa con valori può essere istanziata utilizzando:

def map = [name: "Jerry", age: 42, city: "New York"]

Notare che le chiavi non sono racchiuse tra virgolette.

E per impostazione predefinita Groovy crea un'istanza di java.util.LinkedHashMap . Possiamo sovrascrivere questo comportamento predefinito utilizzando l' operatore as .

3. Aggiunta di elementi

Cominciamo definendo una mappa:

def map = [name:"Jerry"]

Possiamo aggiungere una chiave alla mappa:

map["age"] = 42

Ma un altro modo più simile a Javascript è usare la notazione delle proprietà (l'operatore punto):

map.city = "New York"

In altre parole, Groovy supporta l'accesso a coppie chiave-valore in modo simile a un fagiolo.

Possiamo anche usare variabili invece di letterali come chiavi durante l'aggiunta di nuovi elementi alla mappa:

def hobbyLiteral = "hobby" def hobbyMap = [(hobbyLiteral): "Singing"] map.putAll(hobbyMap) assertTrue(hobbyMap.hobby == "Singing") assertTrue(hobbyMap[hobbyLiteral] == "Singing")

Per prima cosa, dobbiamo creare una nuova variabile che memorizzi l' hobby chiave . Quindi usiamo questa variabile racchiusa tra parentesi con la sintassi letterale della mappa per creare un'altra mappa.

4. Recupero di elementi

La sintassi letterale o la notazione delle proprietà possono essere utilizzate per ottenere elementi da una mappa.

Per una mappa definita come:

def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]

Possiamo ottenere il valore corrispondente al nome della chiave :

assertTrue(map["name"] == "Jerry")

o

assertTrue(map.name == "Jerry")

5. Rimozione di elementi

Possiamo rimuovere qualsiasi voce da una mappa basata su una chiave utilizzando il metodo remove () . Tuttavia, a volte potrebbe essere necessario rimuovere più voci da una mappa. Questo può essere fatto usando il metodo meno () .

Il metodo meno () accetta una mappa . E restituisce una nuova mappa dopo aver rimosso tutte le voci della mappa data dalla mappa sottostante:

def map = [1:20, a:30, 2:42, 4:34, ba:67, 6:39, 7:49] def minusMap = map.minus([2:42, 4:34]); assertTrue(minusMap == [1:20, a:30, ba:67, 6:39, 7:49])

Successivamente, possiamo anche rimuovere le voci in base a una condizione. Ciò può essere ottenuto utilizzando il metodo removeAll () :

minusMap.removeAll{it -> it.key instanceof String} assertTrue(minusMap == [1:20, 6:39, 7:49])

Al contrario, per mantenere tutte le voci che soddisfano una condizione, possiamo utilizzare il metodo keepAll () :

minusMap.retainAll{it -> it.value % 2 == 0} assertTrue(minusMap == [1:20])

6. Iterazione attraverso le voci

Possiamo scorrere le voci utilizzando i metodi each () e eachWithIndex () .

Il metodo each () fornisce parametri impliciti come entry , key e value che corrispondono all'entry corrente .

Il metodo eachWithIndex () fornisce anche un indice oltre a Entry . Entrambi i metodi accettano una chiusura come argomento.

Nel prossimo esempio, iteriamo attraverso ogni voce. Il Closure passato al metodo each () ottiene la coppia chiave-valore dalla voce del parametro implicito e la stampa:

map.each{entry -> println "$entry.key: $entry.value"}

Successivamente, utilizziamo il metodo eachWithIndex () per stampare l'indice corrente insieme ad altri valori:

map.eachWithIndex{entry, i -> println "$i $entry.key: $entry.value"}

È anche possibile richiedere che la chiave , il valore e l'indice vengano forniti separatamente:

map.eachWithIndex{key, value, i -> println "$i $key: $value"}

7. Filtraggio

Possiamo usare i metodi find (), findAll () e grep () per filtrare e cercare le voci della mappa in base a chiavi e valori.

Iniziamo definendo una mappa su cui eseguire questi metodi:

def map = [name:"Jerry", age: 42, city: "New York", hobby:"Singing"]

Per prima cosa, guardiamo il metodo find () che accetta una chiusura e restituisce la prima voce che corrisponde alla condizione di chiusura :

assertTrue(map.find{it.value == "New York"}.key == "city")

Similarly, findAll also accepts a Closure but returns a Map with all the key-value pairs that satisfy the condition in the Closure:

assertTrue(map.findAll{it.value == "New York"} == [city : "New York"])

If we'd prefer to use a List, though, we can use grep instead of findAll:

map.grep{it.value == "New York"}.each{it -> assertTrue(it.key == "city" && it.value == "New York")}

We first used grep to find entries which have the value as New York. Then, to demonstrate the return type is List, we iterate through the result of grep(). And for each Entry in the list which is available in the implicit parameter, we check if its the expected result.

Next, to find out if all the items in a map satisfy a condition we can use every which returns a boolean.

Let's check if all values in the map are of type String:

assertTrue(map.every{it -> it.value instanceof String} == false)

Similarly, we can use any to determine if any items in the map match a condition:

assertTrue(map.any{it -> it.value instanceof String} == true)

8. Transforming and Collecting

At times we may want to transform the entries in a map into new values. Using the collect() and collectEntries() methods it's possible to transform and collect entries into a Collection or Map respectively.

Let's look at some examples.

Given a map of employee ids and employees:

def map = [ 1: [name:"Jerry", age: 42, city: "New York"], 2: [name:"Long", age: 25, city: "New York"], 3: [name:"Dustin", age: 29, city: "New York"], 4: [name:"Dustin", age: 34, city: "New York"]]

We can collect the names of all employees into a list using collect():

def names = map.collect{entry -> entry.value.name} assertTrue(names == ["Jerry", "Long", "Dustin", "Dustin"])

Next, if we're interested in a unique set of names, we can specify the collection by passing a Collection object:

def uniqueNames = map.collect([] as HashSet){entry -> entry.value.name} assertTrue(uniqueNames == ["Jerry", "Long", "Dustin"] as Set)

If we want to change the employee names in the map from lowercase to uppercase, we can use collectEntries. This method returns a map of transformed values:

def idNames = map.collectEntries{key, value -> [key, value.name]} assertTrue(idNames == [1:"Jerry", 2:"Long", 3:"Dustin", 4:"Dustin"])

Lastly, it's also possible to use collect methods in conjunction with the find and findAll methods to transform the filtered results:

def below30Names = map.findAll{it.value.age  value.name} assertTrue(below30Names == ["Long", "Dustin"])

Here, we first find all employees between ages 20-30 and collect them into a map.

9. Grouping

Sometimes we may want to group some items of a map into submaps based on a condition.

The groupBy() method returns a map of maps. And each map contains key-value pairs which evaluate to the same result for the given condition:

def map = [1:20, 2: 40, 3: 11, 4: 93] def subMap = map.groupBy{it.value % 2} assertTrue(subMap == [0:[1:20, 2:40], 1:[3:11, 4:93]])

Another way of creating submaps is by using subMap(). It is different in groupBy() in the sense that it only allows for grouping based on the keys:

def keySubMap = map.subMap([1,2]) assertTrue(keySubMap == [1:20, 2:40])

In this case, the entries for keys 1 and 2 are returned in the new map, and all the other entries are discarded.

10. Sorting

Usually, when sorting, we may want to sort the entries in a map based on key or value or both. Groovy provides a sort() method which can be used for this purpose.

Given a map:

def map = [ab:20, a: 40, cb: 11, ba: 93]

If sorting needs to be done on key, use the no-args sort() method which is based on natural ordering:

def naturallyOrderedMap = map.sort() assertTrue([a:40, ab:20, ba:93, cb:11] == naturallyOrderedMap)

Oppure utilizza il metodo sort (Comparator) per fornire la logica di confronto:

def compSortedMap = map.sort({k1, k2 -> k1  k2} as Comparator) assertTrue([a:40, ab:20, ba:93, cb:11] == compSortedMap)

Successivamente, per ordinare su chiave o valori o entrambi, possiamo fornire una condizione di chiusura a sort () :

def cloSortedMap = map.sort({it1, it2 -> it1.value  it1.value}) assertTrue([cb:11, ab:20, a:40, ba:93] == cloSortedMap)

11. Conclusione

Abbiamo iniziato osservando come creare mappe in Groovy. Successivamente, abbiamo esaminato diversi modi in cui gli elementi possono essere aggiunti, recuperati e rimossi da una mappa.

Successivamente, abbiamo coperto i metodi per eseguire le operazioni comuni che sono forniti immediatamente in Groovy. Includevano il filtraggio, la ricerca, la trasformazione e l'ordinamento.

Come sempre gli esempi trattati nell'articolo si possono trovare su GitHub.