Introduction
Contrairement à d'autres langages, en Go, les dictionnaires (maps) sont des collections non ordonnées. Dans cet exercice pratique, nous allons explorer le tri des dictionnaires et apprendre à les utiliser de manière plus flexible.
Concepts clés :
- Trier des dictionnaires
- Inverser les clés et les valeurs dans un dictionnaire
- Slices de dictionnaires
- Dictionnaires utilisant des slices comme valeurs
- Caractéristiques des dictionnaires en tant que types de référence
Trier des dictionnaires
Créez un fichier map.go dans le répertoire ~/project :
touch ~/project/map.go
Écrivez le code suivant dans le fichier map.go :
package main
import (
"fmt"
)
func main() {
// Déclarer et initialiser une map avec des clés de type string et des valeurs de type int
// La map stocke les noms des étudiants et leurs scores
m := map[string]int{
"Alice": 99, // Chaque paire clé-valeur représente un étudiant et sa note
"Bob": 38,
"Charlie": 84,
}
// Parcourir la map à l'aide d'une boucle for-range
// 'key' représente le nom de l'étudiant, 'value' représente le score
for key, value := range m {
fmt.Println(key, value)
}
fmt.Println("\nInsert Data")
// Démontrer comment ajouter une nouvelle paire clé-valeur à la map
// La syntaxe est : map[key] = value
m["David"] = 25
// Parcourir à nouveau pour afficher le contenu mis à jour de la map
// Notez que l'ordre peut différer à chaque exécution
for key, value := range m {
fmt.Println(key, value)
}
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme peut ressembler à ceci :
Charlie 84
Bob 38
Alice 99
Insert Data
David 25
Charlie 84
Bob 38
Alice 99
Le résultat peut varier car l'ordre des données insérées n'est pas fixe. C'est une caractéristique fondamentale des maps en Go : elles ne garantissent aucun ordre particulier des éléments lors de leur parcours.
Vous pouvez essayer d'exécuter le programme plusieurs fois et observer que l'ordre d'affichage change. Cela illustre le fait qu'on ne peut pas se fier à l'ordre des éléments dans une map.
Cependant, il est parfois nécessaire de trier le dictionnaire après l'insertion des données. Comment y parvenir ?
Puisqu'une map ne peut pas être triée directement, nous pouvons la convertir en une slice, puis trier cette dernière.
Trier par clé
Apprenons d'abord à trier le dictionnaire en fonction de ses clés.
Voici la démarche à suivre :
- Extraire les clés du dictionnaire dans une slice. Une slice est un tableau dynamique ordonné que nous pouvons trier.
- Trier la slice à l'aide du package natif
sortde Go. - Récupérer les valeurs correspondantes en utilisant la méthode de recherche du dictionnaire. Comme nous connaissons l'ordre des clés dans notre slice, nous pouvons interroger la map pour que les valeurs apparaissent selon cet ordre de clés triées.
Écrivez le code suivant dans le fichier map.go :
package main
import (
"fmt"
"sort"
)
func main() {
// Initialiser le dictionnaire
m1 := map[int]string{
3: "Bob",
1: "Alice",
2: "Charlie",
}
keys := make([]int, 0, len(m1)) // Initialiser la slice avec une capacité définie. C'est une optimisation de performance - la slice allouera initialement une mémoire égale à la taille de la map, évitant ainsi des réallocations successives.
for key := range m1 {
// Ajouter la clé à la slice
keys = append(keys, key)
}
// Trier la slice de clés à l'aide du package sort. La fonction `sort.Ints()` trie spécifiquement une slice d'entiers.
sort.Ints(keys)
for _, key := range keys {
// L'affichage est maintenant ordonné
fmt.Println(key, m1[key])
}
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
1 Alice
2 Charlie
3 Bob
Grâce à cette approche, nous avons réussi à trier le dictionnaire sur la base des clés. Les clés sont extraites dans une slice, triées, puis utilisées pour rechercher et afficher les valeurs correspondantes de la map.
Inverser les clés et les valeurs dans un dictionnaire
Avant d'expliquer comment trier par valeur, apprenons à échanger les clés et les valeurs dans un dictionnaire.
L'inversion consiste à permuter les positions des clés et des valeurs, comme illustré dans le schéma ci-dessous :

Le code d'implémentation est simple. Écrivez le code suivant dans le fichier map.go :
package main
import "fmt"
func main() {
m := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
m2 := map[int]string{}
for key, value := range m {
m2[value] = key
}
fmt.Println(m2)
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
map[38:Bob 84:Charlie 99:Alice]
L'essence de ce code est d'extraire les clés et les valeurs du dictionnaire d'origine, puis de les réinsérer dans un nouveau dictionnaire en inversant leurs rôles. C'est simple et direct. Notez que, la map étant non ordonnée, l'ordre de sortie de la map inversée peut varier.
Trier par valeur
En combinant la logique du tri par clé et celle de l'inversion des clés et valeurs, nous pouvons trier un dictionnaire par ses valeurs.
Voici le principe : nous inversons les clés et les valeurs, puis nous utilisons les clés inversées (qui étaient les valeurs d'origine) comme base pour notre tri. Nous utilisons ensuite ces "clés" triées (valeurs d'origine) pour retrouver les clés d'origine correspondantes dans la map inversée.
Écrivez le code suivant dans le fichier map.go :
package main
import (
"fmt"
"sort"
)
func main() {
// Initialiser le dictionnaire
m1 := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
// Initialiser le dictionnaire inversé
m2 := map[int]string{}
for key, value := range m1 {
// Générer le dictionnaire inversé m2 en échangeant les paires clé-valeur
m2[value] = key
}
values := make([]int, 0) // Initialiser la slice pour le tri
for _, value := range m1 {
// Convertir les valeurs du dictionnaire d'origine en slice
values = append(values, value)
}
// Trier la slice de valeurs à l'aide du package sort
sort.Ints(values)
for _, value := range values {
// L'affichage est maintenant ordonné selon les valeurs
fmt.Println(m2[value], value)
}
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
Bob 38
Charlie 84
Alice 99
Nous avons maintenant trié le dictionnaire en fonction de ses valeurs. Nous avons converti les valeurs en slice, trié la slice, puis utilisé ces valeurs triées pour récupérer et afficher les clés d'origine correspondantes à partir de la map inversée.
Trier avec sort.Slice
Si votre version de Go est supérieure à 1.7, vous pouvez utiliser la fonction sort.Slice pour trier rapidement une map par clé ou par valeur. sort.Slice vous permet de spécifier une fonction de comparaison personnalisée.
Voici un exemple :
package main
import (
"fmt"
"sort"
)
func main() {
m1 := map[int]int{
21: 99,
12: 98,
35: 17,
24: 36,
}
type kv struct {
Key int
Value int
}
var s1 []kv
for k, v := range m1 {
s1 = append(s1, kv{k, v})
}
sort.Slice(s1, func(i, j int) bool {
return s1[i].Key < s1[j].Key
})
fmt.Println("Sorted in ascending order by key:")
for _, pair := range s1 {
fmt.Printf("%d, %d\n", pair.Key, pair.Value)
}
}
Exécutez le programme :
go run ~/project/map.go
Le résultat est le suivant :
Sorted in ascending order by key:
12, 98
21, 99
24, 36
35, 17
Dans ce programme, nous avons utilisé une structure kv pour stocker les paires clé-valeur de la map. Nous avons ensuite trié la slice de structures à l'aide de la fonction sort.Slice() et d'une fonction de comparaison anonyme. Cette fonction de comparaison (func(i, j int) bool) détermine l'ordre de tri en se basant sur le champ Key de notre structure.
Nous pouvons également utiliser sort.Slice pour trier la map par ordre décroissant des clés ou par ordre croissant des valeurs en modifiant simplement cette fonction de comparaison. Cela offre une grande flexibilité dans la manière de trier les données d'une map.
Petit test
Créez un fichier map2.go et modifiez le code de la section précédente pour trier la map par ordre décroissant en fonction de la valeur.
Résultat attendu :
Exécutez le programme :
go run ~/project/map2.go
Sorted in descending order by value:
21, 99
12, 98
24, 36
35, 17
Contraintes :
- Le fichier
map2.godoit être placé dans le répertoire~/project. - Vous devez utiliser la fonction
sort.Slice. Vous devrez modifier la fonction de comparaison utilisée dans l'exemple précédent.
Slices de dictionnaires
Tout comme nous pouvons utiliser des tableaux ou des slices pour stocker des données liées, nous pouvons également utiliser des slices dont les éléments sont des dictionnaires. Cela nous permet de gérer des collections de données structurées sous forme de maps.
Écrivez le code suivant dans le fichier map.go :
package main
import "fmt"
func main() {
// Déclarer une slice de maps et l'initialiser avec make
var mapSlice = make([]map[string]string, 3) // Crée une slice d'une capacité de 3, où chaque élément peut être une `map[string]string`.
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
fmt.Println("Initialization")
// Assigner des valeurs au premier élément de la slice
mapSlice[0] = make(map[string]string, 10) // Crée une map au premier index.
mapSlice[0]["name"] = "labex"
mapSlice[0]["password"] = "123456"
mapSlice[0]["address"] = "Paris"
for index, value := range mapSlice {
fmt.Printf("index:%d value:%v\n", index, value)
}
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
index:0 value:map[]
index:1 value:map[]
index:2 value:map[]
Initialization
index:0 value:map[address:Paris name:labex password:123456]
index:1 value:map[]
index:2 value:map[]
Ce code illustre l'initialisation d'une slice de maps. Au départ, chaque élément de la slice est une map vide. Nous créons ensuite et assignons une map au premier élément, en la remplissant de données. Cela montre comment gérer une liste de maps.
Dictionnaires avec des slices comme valeurs
Nous pouvons également utiliser des dictionnaires dont les valeurs sont des slices pour stocker davantage de données. Cela permet d'associer plusieurs valeurs à une seule clé dans une map, créant ainsi une relation de type "un-à-plusieurs".
Écrivez le code suivant dans le fichier map.go :
package main
import "fmt"
func main() {
var sliceMap = make(map[string][]string, 3) // Déclare une map où les clés sont des chaînes et les valeurs sont des slices de chaînes. Le 3 est une indication de capacité.
key := "labex"
value, ok := sliceMap[key] // Vérifie si la clé existe
if !ok {
value = make([]string, 0, 2) // Si elle n'existe pas, initialise une nouvelle slice avec une capacité.
}
value = append(value, "Paris", "Shanghai") // Ajoute des valeurs à la slice.
sliceMap[key] = value // Définit la slice comme valeur pour notre clé dans la map.
fmt.Println(sliceMap)
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
map[labex:[Paris Shanghai]]
Le code déclare d'abord un type de map dont les valeurs sont des slices. Il vérifie si la clé existe avant d'ajouter de nouveaux éléments à la slice associée, illustrant un modèle courant pour manipuler des maps de slices.
Caractéristiques des dictionnaires en tant que types de référence
Les tableaux sont des types de valeur, donc les assigner ou les passer à des fonctions crée une copie. Les modifications apportées à la copie n'affectent pas le tableau d'origine. En revanche, les maps sont des types de référence. Cela signifie que l'assignation ou le passage d'une map à une fonction ne crée pas une copie complète. Au lieu de cela, la map est passée par référence.
C'est un point crucial, car les modifications effectuées à l'intérieur de la fonction affecteront les données de la map d'origine.
Écrivez le code suivant dans le fichier map.go :
package main
import "fmt"
func modifyMap(x map[string]int) {
x["Bob"] = 100 // Modifie la map qui a été passée en argument.
}
func main() {
a := map[string]int{
"Alice": 99,
"Bob": 38,
"Charlie": 84,
}
// Comme la map est passée par référence, la modification dans modifyMap change le dictionnaire d'origine
modifyMap(a)
fmt.Println(a) // map[Alice:99 Bob:100 Charlie:84]
}
Exécutez le programme :
go run ~/project/map.go
La sortie du programme est la suivante :
map[Alice:99 Bob:100 Charlie:84]
Dans cet exemple, nous avons démontré la caractéristique de transfert par référence d'un dictionnaire. La fonction modifyMap modifie la map d'origine car a est une référence aux mêmes données sous-jacentes. Il est essentiel de comprendre ce comportement lors du passage de maps à des fonctions.
Résumé
Dans ce lab, nous avons exploré l'utilisation avancée des maps en Go, notamment :
- Le tri des maps par clé ou par valeur, ce qui nécessite de convertir la map en slice avant de trier cette dernière.
- L'inversion des clés et des valeurs dans une map pour créer un nouveau dictionnaire aux rôles inversés.
- L'utilisation de slices de maps, permettant de créer des collections de données cartographiées.
- L'utilisation de maps avec des slices comme valeurs, permettant d'associer plusieurs valeurs à une clé unique.
- La nature de type de référence des maps, où les modifications apportées à une map transmise se répercutent sur l'original.
La maîtrise de ces concepts vous permettra d'utiliser les maps de Go de manière plus efficace dans vos applications réelles.



