Sortieren von Go-Dictionaries

GolangBeginner
Jetzt üben

Einführung

Im Gegensatz zu vielen anderen Programmiersprachen sind Dictionaries (Maps) in Go ungeordnete Sammlungen. In diesem Lab lernen wir, wie man Dictionaries sortiert und wie man sie flexibler einsetzt.

Kernkonzepte:

  • Sortieren von Dictionaries
  • Vertauschen von Schlüsseln und Werten in einem Dictionary
  • Slices von Dictionaries
  • Dictionaries mit Slices als Werten
  • Referenztyp-Eigenschaften von Dictionaries

Dictionaries sortieren

Erstellen Sie eine Datei namens map.go im Verzeichnis ~/project:

touch ~/project/map.go

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import (
 "fmt"
)

func main() {
 // Deklaration und Initialisierung einer Map mit String-Schlüsseln und Integer-Werten
 // Die Map speichert Studentennamen und deren Punktzahlen
 m := map[string]int{
  "Alice":   99, // Jedes Schlüssel-Wert-Paar repräsentiert einen Studenten und seine Punktzahl
  "Bob":     38,
  "Charlie": 84,
 }

 // Iteration durch die Map mit einer for-range-Schleife
 // 'key' steht für die Studentennamen, 'value' für die Punktzahlen
 for key, value := range m {
  fmt.Println(key, value)
 }

 fmt.Println("\nInsert Data")

 // Demonstration, wie ein neues Schlüssel-Wert-Paar zur Map hinzugefügt wird
 // Die Syntax lautet: map[key] = value
 m["David"] = 25

 // Erneute Iteration, um den aktualisierten Inhalt der Map anzuzeigen
 // Beachten Sie, dass die Reihenfolge bei jeder Ausführung anders sein kann
 for key, value := range m {
  fmt.Println(key, value)
 }
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms könnte etwa so aussehen:

Charlie 84
Bob 38
Alice 99

Insert Data
David 25
Charlie 84
Bob 38
Alice 99

Die Ausgabe kann variieren, da die Reihenfolge der eingefügten Daten nicht festgelegt ist. Dies ist eine Kerneigenschaft von Go-Maps – sie garantieren keine bestimmte Reihenfolge der Elemente, wenn man über sie iteriert.

Sie können versuchen, das Programm mehrmals auszuführen, und werden feststellen, dass sich die Reihenfolge ändern kann. Dies verdeutlicht, dass man sich nicht auf die Sortierung der Elemente in einer Map verlassen darf.

Manchmal ist es jedoch erforderlich, ein Dictionary nach dem Einfügen der Daten zu sortieren. Wie lässt sich das umsetzen?

Da eine Map selbst nicht sortiert werden kann, konvertieren wir die Map in ein Slice und sortieren anschließend das Slice.

Nach Schlüssel sortieren

Zuerst lernen wir, wie man ein Dictionary nach seinen Schlüsseln sortiert.

Dies sind die Schritte:

  • Konvertieren Sie die Schlüssel des Dictionarys in ein Slice. Ein Slice ist ein geordnetes, dynamisches Array, das sortiert werden kann.
  • Sortieren Sie das Slice mithilfe des integrierten sort-Pakets von Go.
  • Rufen Sie die entsprechenden Werte über die Lookup-Methode des Dictionarys ab. Da wir nun die Reihenfolge der Schlüssel im Slice kennen, können wir die Werte in der Map nachschlagen, sodass sie in der sortierten Reihenfolge der Schlüssel erscheinen.

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import (
 "fmt"
 "sort"
)

func main() {
 // Initialisierung des Dictionarys
 m1 := map[int]string{
  3: "Bob",
  1: "Alice",
  2: "Charlie",
 }
 keys := make([]int, 0, len(m1)) // Initialisierung des Slices mit Kapazität. Dies ist eine Performance-Optimierung - das Slice reserviert sofort Speicher in der Größe der Map, um Neuzuweisungen zu vermeiden.
 for key := range m1 {
  // Schlüssel in das Slice übertragen
  keys = append(keys, key)
 }
 // Sortieren des Schlüssel-Slices mit dem sort-Paket. Die Funktion `sort.Ints()` sortiert speziell Slices von Integern.
 sort.Ints(keys)
 for _, key := range keys {
  // Die Ausgabe erfolgt nun sortiert
  fmt.Println(key, m1[key])
 }
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

1 Alice
2 Charlie
3 Bob

Mit diesem Ansatz haben wir eine Sortierung des Dictionarys basierend auf dem Schlüssel erreicht. Die Schlüssel werden in ein Slice extrahiert, sortiert und dann verwendet, um die zugehörigen Werte aus der Map in der richtigen Reihenfolge auszulesen.

Schlüssel und Werte in einem Dictionary vertauschen

Bevor wir erklären, wie man nach Werten sortiert, schauen wir uns an, wie man Schlüssel und Werte in einem Dictionary vertauscht.

Das Vertauschen bedeutet, dass die Positionen von Schlüsseln und Werten im Dictionary gegeneinander ausgewechselt werden, wie in der folgenden Abbildung dargestellt:

Dictionary key value swap

Der Implementierungscode ist simpel. Schreiben Sie den folgenden Code in die Datei 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)
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

map[38:Bob 84:Charlie 99:Alice]

Der Kern dieses Codes besteht darin, die Schlüssel und Werte aus dem ursprünglichen Dictionary zu extrahieren und sie mit vertauschten Rollen wieder in ein neues Dictionary einzufügen. Da Maps ungeordnet sind, kann die Reihenfolge der Ausgabe des vertauschten Dictionarys variieren.

Nach Wert sortieren

Indem wir die Logik der Sortierung nach Schlüsseln mit dem Vertauschen von Schlüsseln und Werten kombinieren, können wir ein Dictionary nach seinen Werten sortieren.

So funktioniert es: Wir vertauschen Schlüssel und Werte und behandeln dann die vertauschten Schlüssel (die ursprünglichen Werte) als Basis für unsere Sortierung. Anschließend nutzen wir die sortierten "Schlüssel" (die ursprünglichen Werte), um die entsprechenden ursprünglichen Schlüssel im vertauschten Dictionary nachzuschlagen.

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import (
 "fmt"
 "sort"
)

func main() {
 // Initialisierung des Dictionarys
 m1 := map[string]int{
  "Alice":   99,
  "Bob":     38,
  "Charlie": 84,
 }

 // Initialisierung des umgekehrten Dictionarys
 m2 := map[int]string{}
 for key, value := range m1 {
  // Erzeugen des umgekehrten Dictionarys m2 durch Vertauschen der Schlüssel-Wert-Paare
  m2[value] = key
 }

 values := make([]int, 0) // Initialisierung des Slices für die Sortierung
 for _, value := range m1 {
  // Werte des ursprünglichen Dictionarys in ein Slice konvertieren
  values = append(values, value)
 }
 // Sortieren des Wert-Slices mit dem sort-Paket
 sort.Ints(values)

 for _, value := range values {
  // Die Ausgabe erfolgt nun sortiert nach Werten
  fmt.Println(m2[value], value)
 }
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

Bob 38
Charlie 84
Alice 99

Nun haben wir das Dictionary basierend auf seinen Werten sortiert. Wir haben die Werte in ein Slice konvertiert, dieses sortiert und dann die sortierten Werte verwendet, um die ursprünglichen Schlüssel aus der vertauschten Map abzurufen.

Sortieren mit sort.Slice

Wenn die Go-Version neuer als 1.7 ist, können wir die Funktion sort.Slice verwenden, um eine Map schnell nach Schlüssel oder Wert zu sortieren. sort.Slice ermöglicht die Angabe einer benutzerdefinierten Vergleichsfunktion.

Hier ist ein Beispiel:

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

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe sieht wie folgt aus:

Sorted in ascending order by key:
12, 98
21, 99
24, 36
35, 17

In diesem Programm haben wir ein Struct kv verwendet, um die Schlüssel-Wert-Paare der Map zu speichern. Anschließend haben wir das Slice aus Structs mit der Funktion sort.Slice() und einer anonymen Vergleichsfunktion sortiert. Diese Vergleichsfunktion (func(i, j int) bool) bestimmt die Sortierreihenfolge basierend auf dem Feld Key unseres Structs.

Wir können sort.Slice auch verwenden, um die Map absteigend nach Schlüsseln oder aufsteigend nach Werten zu sortieren, indem wir einfach die Vergleichsfunktion anpassen. Dies bietet eine große Flexibilität bei der Sortierung von Map-Daten.

Kleiner Test

Erstellen Sie eine Datei map2.go und ändern Sie den Code aus dem vorherigen Abschnitt so ab, dass die Map absteigend basierend auf dem Wert sortiert wird.

Erwartete Ausgabe:

Führen Sie das Programm aus:

go run ~/project/map2.go
Sorted in descending order by value:
21, 99
12, 98
24, 36
35, 17

Anforderungen:

  • Die Datei map2.go muss im Verzeichnis ~/project liegen.
  • Sie müssen die Funktion sort.Slice verwenden. Sie müssen die Vergleichsfunktion in sort.Slice gegenüber dem vorherigen Beispiel anpassen.

Slices von Dictionaries

Genauso wie wir Arrays oder Slices verwenden können, um zusammengehörige Daten zu speichern, können wir auch Slices verwenden, deren Elemente Dictionaries sind. Dies ermöglicht es uns, Sammlungen von Map-Daten zu verwalten, was besonders nützlich für strukturierte Informationen ist.

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import "fmt"

func main() {
 // Deklaration eines Slices von Maps und Initialisierung mit make
 var mapSlice = make([]map[string]string, 3) // Erstellt ein Slice mit einer Kapazität von 3, wobei jedes Element eine `map[string]string` sein kann.
 for index, value := range mapSlice {
  fmt.Printf("index:%d value:%v\n", index, value)
 }
 fmt.Println("Initialization")
 // Zuweisung von Werten zum ersten Element des Slices
 mapSlice[0] = make(map[string]string, 10) // Erstellt eine Map am ersten 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)
 }
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

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

Der Code demonstriert die Initialisierung eines Slices von Maps. Zu Beginn ist jedes Element im Slice eine leere Map. Wir erstellen dann eine Map, weisen sie dem ersten Element zu und füllen sie mit Daten. Dies zeigt, wie man eine Liste von Maps verwaltet.

Dictionaries mit Slices als Werten

Wir können auch Dictionaries verwenden, die Slices als Werte enthalten, um mehr Daten in einem Dictionary zu speichern. Dies erlaubt es uns, einem einzelnen Schlüssel in einer Map mehrere Werte zuzuordnen, was effektiv eine "Eins-zu-viele"-Beziehung schafft.

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import "fmt"

func main() {
 var sliceMap = make(map[string][]string, 3) // Deklariert eine Map, in der Schlüssel Strings und Werte Slices von Strings sind. Die 3 ist ein Kapazitätshinweis.
 key := "labex"
 value, ok := sliceMap[key]  // Prüft, ob der Schlüssel existiert
 if !ok {
  value = make([]string, 0, 2) // Falls nicht, wird ein neues Slice mit Kapazität initialisiert.
 }
 value = append(value, "Paris", "Shanghai") // Fügt Werte zum Slice hinzu.
 sliceMap[key] = value // Setzt das Slice als Wert für unseren Schlüssel in der Map.
 fmt.Println(sliceMap)
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

map[labex:[Paris Shanghai]]

Der Code deklariert zunächst einen Map-Typ, bei dem die Werte Slices sind. Er prüft, ob der Schlüssel bereits existiert, bevor er neue Elemente zum zugehörigen Slice hinzufügt – ein gängiges Muster beim Umgang mit Maps von Slices.

Referenztyp-Eigenschaften von Dictionaries

Arrays sind Werttypen, daher wird beim Zuweisen oder Übergeben an Funktionen eine Kopie erstellt. Änderungen an der Kopie wirken sich nicht auf das ursprüngliche Array aus. Maps hingegen sind Referenztypen. Das bedeutet, dass beim Zuweisen oder Übergeben einer Map an eine Funktion keine vollständige Kopie erstellt wird. Stattdessen wird die Map per Referenz übergeben.

Dies ist wichtig, da Änderungen innerhalb der Funktion die ursprünglichen Map-Daten tatsächlich verändern.

Schreiben Sie den folgenden Code in die Datei map.go:

package main

import "fmt"

func modifyMap(x map[string]int) {
 x["Bob"] = 100 // Modifiziert die Map, die als Argument übergeben wurde.
}

func main() {
 a := map[string]int{
  "Alice":   99,
  "Bob":     38,
  "Charlie": 84,
 }
 // Da die Map per Referenz übergeben wird, ändert die Modifikation in modifyMap das ursprüngliche Dictionary
 modifyMap(a)
 fmt.Println(a) // map[Alice:99 Bob:100 Charlie:84]
}

Führen Sie das Programm aus:

go run ~/project/map.go

Die Ausgabe des Programms sieht wie folgt aus:

map[Alice:99 Bob:100 Charlie:84]

In diesem Beispiel haben wir die Eigenschaft der Referenzübertragung eines Dictionarys demonstriert. Die Funktion modifyMap ändert die ursprüngliche Map, da a eine Referenz auf dieselben zugrunde liegenden Map-Daten ist. Dieses Verhalten ist entscheidend zu verstehen, wenn man Maps an Funktionen übergibt.

Zusammenfassung

In diesem Lab haben wir fortgeschrittene Anwendungen von Maps in Go kennengelernt, darunter:

  • Das Sortieren von Maps nach Schlüssel oder Wert, was die Konvertierung der Map in ein Slice und das anschließende Sortieren des Slices erfordert.
  • Das Vertauschen von Schlüsseln und Werten in einer Map, wodurch eine neue Map mit umgekehrten Rollen entsteht.
  • Die Verwendung von Slices von Maps, was die Erstellung von Sammlungen zusammengehöriger Map-Daten ermöglicht.
  • Die Verwendung von Maps mit Slices als Werten, um einem einzelnen Schlüssel mehrere Werte zuzuordnen.
  • Die Referenztyp-Eigenschaften von Maps, bei denen Änderungen an einer übergebenen Map direkt in der ursprünglichen Map reflektiert werden.

Das Verständnis dieser Konzepte wird es Ihnen ermöglichen, Go-Maps in realen Anwendungen effektiver einzusetzen.

✨ Lösung prüfen und üben