HOWTO · Go

Einen Slice aus Structs in Go sortieren

Dieses Tutorial zeigt, wie Sie den Slice von Strukturen in GoLang sortieren.

Auf dieser Seite

Verwenden Sie das Standardpaket sort, um einen Slice aus Structs zu sortieren. sort.Slice erhält den Slice und eine Vergleichsfunktion, der zwei Indizes übergeben werden. Vergleichen Sie die Felder von slice[i] und slice[j]. Der Slice wird direkt sortiert. Wenn Sie < durch > ersetzen, wird aus aufsteigender eine absteigende Sortierung.

Die Beispiele zeigen den Slice nach dem jeweiligen Sortiervorgang. Da direkt im Slice sortiert wird, sollten Sie ihn vorher kopieren, wenn Sie die ursprüngliche Reihenfolge noch benötigen. Eine Kopie, die nur den Slice-Header übernimmt, bewahrt keine unabhängige Reihenfolge.

Einen Slice aus Structs mit sort.Slice nach einem Feld sortieren

Dieses vollständige Beispiel sortiert Beschäftigte nach ihrem Gehalt, vom niedrigsten zum höchsten. Die Vergleichsfunktion greift über den jeweiligen Index im ursprünglichen Slice auf jeden Beschäftigten zu.

package main

import (
	"fmt"
	"sort"
)

type Employee struct {
	Name   string
	Salary int
}

func main() {
	employees := []Employee{
		{Name: "John", Salary: 1500},
		{Name: "Joe", Salary: 3000},
		{Name: "Jack", Salary: 3400},
	}

	sort.Slice(employees, func(i, j int) bool {
		return employees[i].Salary < employees[j].Salary
	})

	fmt.Println(employees)
}
[{John 1500} {Joe 3000} {Jack 3400}]

Der Vergleich mit < setzt das niedrigere Gehalt an den Anfang. Verwenden Sie > für eine Sortierung vom höchsten zum niedrigsten Gehalt. sort.Slice verändert den übergebenen Slice und garantiert nicht, dass Datensätze mit gleichem Gehalt ihre Reihenfolge beibehalten.

Die Reihenfolge gleicher Schlüssel mit sort.SliceStable erhalten

sort.SliceStable sortiert Structs ebenfalls mit einer indexbasierten Vergleichsfunktion. Die Stabilitätsgarantie bedeutet, dass Datensätze mit gleichen Schlüsseln ihre relative Reihenfolge aus dem Eingabe-Slice behalten. Das ist nützlich, wenn diese Reihenfolge Bedeutung hat, etwa durch eine frühere Rangfolge oder die Reihenfolge beim Import.

package main

import (
	"fmt"
	"sort"
)

type Employee struct {
	Name   string
	Salary int
}

func main() {
	employees := []Employee{
		{Name: "Zoe", Salary: 3000},
		{Name: "John", Salary: 1500},
		{Name: "Ava", Salary: 3000},
	}

	sort.SliceStable(employees, func(i, j int) bool {
		return employees[i].Salary < employees[j].Salary
	})

	fmt.Println(employees)
}
[{John 1500} {Zoe 3000} {Ava 3000}]

Der Gehaltsvergleich sortiert die Datensätze nach Gehalt und lässt Zoe vor Ava stehen, weil beide gleich viel verdienen und Zoe zuvor im Slice stand. Stabiles Sortieren funktioniert mit Structs ebenso wie mit anderen Elementtypen eines Slice.

Nach mehreren Feldern mit einem Gleichstandsmerkmal sortieren

Um zuerst nach einem Hauptfeld und anschließend nach einem zweiten Feld zu sortieren, vergleichen Sie zunächst die Werte des Hauptfelds. Sind sie gleich, vergleichen Sie die Werte des zweiten Felds. Dieses Beispiel sortiert Positionen alphabetisch und innerhalb jeder Position die Namen ebenfalls alphabetisch.

package main

import (
	"fmt"
	"sort"
)

type Employee struct {
	Name     string
	Position string
}

func main() {
	employees := []Employee{
		{Name: "Michael", Position: "Developer"},
		{Name: "Jack", Position: "Manager"},
		{Name: "Joe", Position: "CEO"},
		{Name: "Leonard", Position: "Intern"},
		{Name: "Sheldon", Position: "Developer"},
	}

	sort.Slice(employees, func(i, j int) bool {
		if employees[i].Position != employees[j].Position {
			return employees[i].Position < employees[j].Position
		}
		return employees[i].Name < employees[j].Name
	})

	fmt.Println(employees)
}
[{Joe CEO} {Michael Developer} {Sheldon Developer} {Leonard Intern} {Jack Manager}]

Der Vergleich muss konsistent sein: Zuerst wird nach Position sortiert, und Name wird nur verglichen, wenn die Positionen übereinstimmen. Der zweite Vergleich dient als Gleichstandsmerkmal; beide Vergleiche sortieren aufsteigend. sort.SliceStable ist hier nicht erforderlich, da der Vergleich selbst die Reihenfolge innerhalb jeder Position festlegt.

sort.Interface für wiederverwendbares Sortieren implementieren

Wenn Sie eine Sortierregel wiederverwenden möchten, definieren Sie einen benannten Slice-Typ und implementieren sort.Interface. Len gibt die Anzahl der Elemente zurück, Less legt ihre Reihenfolge fest und Swap vertauscht zwei Elemente. Übergeben Sie den Slice anschließend an sort.Sort.

package main

import (
	"fmt"
	"sort"
)

type Employee struct {
	Name   string
	Salary int
}

type BySalary []Employee

func (employees BySalary) Len() int           { return len(employees) }
func (employees BySalary) Swap(i, j int)      { employees[i], employees[j] = employees[j], employees[i] }
func (employees BySalary) Less(i, j int) bool { return employees[i].Salary < employees[j].Salary }

func main() {
	employees := BySalary{
		{Name: "John", Salary: 1500},
		{Name: "Joe", Salary: 3000},
		{Name: "Jack", Salary: 3400},
	}

	sort.Sort(employees)
	fmt.Println(employees)
}
[{John 1500} {Joe 3000} {Jack 3400}]

Hier implementiert BySalary die drei erforderlichen Methoden, sodass sort.Sort den Slice nach Gehalt sortieren kann. Dieses Muster eignet sich, wenn dieselbe Sortierregel einen Namen erhalten und an mehreren Stellen wiederverwendet werden soll oder wenn die Sortierlogik zu einem benutzerdefinierten Sammlungstyp gehört. Für einen einmaligen Vergleich ist sort.Slice meist kürzer.

Verwenden Sie sort.Slice, wenn die Sortierung nur für einen Vorgang gilt oder davon abhängt, welches Feld der Aufrufer gerade auswählt. sort.Interface eignet sich, wenn eine benannte Sortierung an mehreren Stellen wiederverwendet wird oder zu einer Sammlung gehört. Entscheiden Sie in beiden Fällen, ob Datensätze mit gleichen Schlüsseln ihre relative Reihenfolge ändern dürfen. Wenn die ursprüngliche Reihenfolge erhalten bleiben muss, wählen Sie sort.SliceStable.