Cartes à Groovy

1. Vue d'ensemble

Groovy étend l' API Map en Java pour fournir des méthodes pour des opérations telles que le filtrage, la recherche et le tri . Il fournit également une variété de méthodes abrégées pour créer et manipuler des cartes.

Dans cet article, nous examinerons la manière Groovy de travailler avec les cartes.

2. Création de Groovy Map s

Nous pouvons utiliser la syntaxe littérale de carte [k: v] pour créer des cartes. Fondamentalement, cela nous permet d'instancier une carte et de définir des entrées sur une seule ligne.

Une carte vide peut être créée en utilisant:

def emptyMap = [:]

De même, une carte avec des valeurs peut être instanciée en utilisant:

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

Notez que les clés ne sont pas entourées de guillemets.

Et par défaut, Groovy crée une instance de java.util.LinkedHashMap . Nous pouvons remplacer ce comportement par défaut en utilisant l' opérateur as .

3. Ajout d'éléments

Commençons par définir une carte:

def map = [name:"Jerry"]

Nous pouvons ajouter une clé à la carte:

map["age"] = 42

Mais une autre façon plus semblable à Javascript consiste à utiliser la notation de propriété (l'opérateur point):

map.city = "New York"

En d'autres termes, Groovy prend en charge l'accès aux paires clé-valeur à la manière d'un bean.

Nous pouvons également utiliser des variables au lieu de littéraux comme clés lors de l'ajout de nouveaux éléments à la carte:

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

Tout d'abord, nous devons créer une nouvelle variable qui stocke le passe-temps clé . Ensuite, nous utilisons cette variable entre parenthèses avec la syntaxe littérale de la carte pour créer une autre carte.

4. Récupération des éléments

La syntaxe littérale ou la notation de propriété peut être utilisée pour obtenir des éléments d'une carte.

Pour une carte définie comme:

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

Nous pouvons obtenir la valeur correspondant au nom de la clé :

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

ou

assertTrue(map.name == "Jerry")

5. Suppression d'éléments

Nous pouvons supprimer n'importe quelle entrée d'une carte basée sur une clé en utilisant la méthode remove () . Mais, parfois, nous pouvons avoir besoin de supprimer plusieurs entrées d'une carte. Cela peut être fait en utilisant la méthode minus () .

La méthode minus () accepte un Map . Et renvoie une nouvelle carte après avoir supprimé toutes les entrées de la carte donnée de la carte sous-jacente:

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])

Ensuite, nous pouvons également supprimer des entrées en fonction d'une condition. Cela peut être réalisé en utilisant la méthode removeAll () :

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

Inversement, pour conserver toutes les entrées qui satisfont à une condition, nous pouvons utiliser la méthode retentionAll () :

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

6. Itération des entrées

Nous pouvons parcourir les entrées en utilisant les méthodes each () et eachWithIndex () .

La méthode each () fournit des paramètres implicites tels que l' entrée , la clé et la valeur qui correspondent à l' entrée actuelle .

La méthode eachWithIndex () fournit également un index en plus de Entry . Les deux méthodes acceptent une fermeture comme argument.

Dans l'exemple suivant, nous parcourons chaque entrée. La fermeture transmise à la méthode each () obtient la paire clé-valeur à partir de l'entrée de paramètre implicite et l'imprime:

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

Ensuite, nous utilisons la méthode eachWithIndex () pour imprimer l'index actuel avec d'autres valeurs:

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

Il est également possible de demander que la clé , la valeur et l'index soient fournis séparément:

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

7. Filtrage

Nous pouvons utiliser les méthodes find (), findAll () et grep () pour filtrer et rechercher des entrées de carte en fonction des clés et des valeurs.

Commençons par définir une carte pour exécuter ces méthodes sur:

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

Tout d'abord, nous examinons la méthode find () qui accepte une fermeture et retourne la première entrée qui correspond à la condition de fermeture :

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)

Ou utilisez la méthode sort (Comparator) pour fournir une logique de comparaison:

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

Ensuite, pour trier sur la clé ou les valeurs ou les deux, nous pouvons fournir une condition de fermeture à sort () :

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

11. Conclusion

Nous avons commencé par examiner comment créer des cartes dans Groovy. Ensuite, nous avons examiné différentes façons dont les éléments peuvent être ajoutés, récupérés et supprimés d'une carte.

Plus tard, nous avons couvert les méthodes pour effectuer des opérations courantes qui sont fournies prêtes à l'emploi dans Groovy. Ils comprenaient le filtrage, la recherche, la transformation et le tri.

Comme toujours, les exemples couverts dans l'article peuvent être trouvés sur GitHub.