[UDPATE] C'est ma faute. J'aurais dû lire les docs plus attentivement que de poser cette question tout de suite. Ma faute.
J'ai un tableau/tranche de membres:
type Member struct {
Id int
LastName string
FirstName string
}
var members []Member
Ma question est de savoir comment les trier par LastName
et ensuite par FirstName
.
Toute aide est appréciée merci.
Utilisez la fonction sort.Slice (disponible depuis Go 1.8) ou la fonction sort.Sort pour trier une tranche de valeurs.
Avec les deux fonctions, l'application fournit une fonction qui teste si un élément de tranche est inférieur à un autre élément de tranche. Pour trier par nom de famille, puis par prénom, comparez le nom de famille et le prénom:
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
La fonction less est spécifiée à l'aide d'une fonction anonyme avec sort.Slice:
var members []Member
sort.Slice(members, func(i, j int) bool {
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
})
La fonction less est spécifiée avec une interface - avec la fonction sort.Sort:
type byLastFirst []Member
func (members byLastFirst) Len() int { return len(members) }
func (members byLastFirst) Swap(i, j int) { members[i], members[j] = members[j], members[i] }
func (members byLastFirst) Less(i, j int) bool {
if members[i].LastName < members[j].LastName {
return true
}
if members[i].LastName > members[j].LastName {
return false
}
return members[i].FirstName < members[j].FirstName
}
sort.Sort(byLastFirst(members))
Sauf si l'analyse des performances montre que le tri est un point chaud, utilisez la fonction la plus pratique pour votre application.
Utilisez la nouvelle fonction sort.Slice
en tant que telle:
sort.Slice(members, func(i, j int) bool {
switch strings.Compare(members[i].FirstName, members[j].FirstName) {
case -1:
return true
case 1:
return false
}
return members[i].LastName > members[j].LastName
})
ou quelque chose comme ça.
Un autre motif, que je trouve légèrement plus propre:
if members[i].LastName != members[j].LastName {
return members[i].LastName < members[j].LastName
}
return members[i].FirstName < members[j].FirstName
Le code le plus court et le plus compréhensible que j'ai réussi à écrire est le suivant:
package main
import (
"fmt"
"sort"
)
type Member struct {
Id int
LastName string
FirstName string
}
func sortByLastNameAndFirstName(members []Member) {
sort.SliceStable(members, func(i, j int) bool {
mi, mj := members[i], members[j]
switch {
case mi.LastName != mj.LastName:
return mi.LastName < mj.LastName
default:
return mi.FirstName < mj.FirstName
}
})
}
Le modèle utilisant l'instruction switch
s'étend facilement à plus de deux critères de tri et reste suffisamment court pour être lu.
Voici le reste du programme:
func main() {
members := []Member{
{0, "The", "quick"},
{1, "brown", "fox"},
{2, "jumps", "over"},
{3, "brown", "grass"},
{4, "brown", "grass"},
{5, "brown", "grass"},
{6, "brown", "grass"},
{7, "brown", "grass"},
{8, "brown", "grass"},
{9, "brown", "grass"},
{10, "brown", "grass"},
{11, "brown", "grass"},
}
sortByLastNameAndFirstNameFunctional(members)
for _, member := range members {
fmt.Println(member)
}
}
Si vous voulez éviter de mentionner les champs LastName
et FirstName
plusieurs fois et si vous voulez éviter de mélanger i
et j
(ce qui peut arriver tout le temps), j'ai joué un peu et l'idée de base est la suivante:
func sortByLastNameAndFirstNameFunctional(members []Member) {
NewSorter().
AddStr(member -> member.LastName).
AddStr(member -> member.FirstName).
AddInt(member -> member.Id).
SortStable(members)
}
Puisque Go ne prend pas en charge l'opérateur ->
pour la création de fonctions anonymes et qu'il ne possède pas non plus de médicaments génériques tels que Java, un peu de surcharge syntaxique est nécessaire:
func sortByLastNameAndFirstNameFunctional(members []Member) {
NewSorter().
AddStr(func(i interface{}) string { return i.(Member).LastName }).
AddStr(func(i interface{}) string { return i.(Member).FirstName }).
AddInt(func(i interface{}) int { return i.(Member).Id}).
SortStable(members)
}
L’implémentation, ainsi que l’API, est un peu moche en utilisant interface{}
et réflexion, mais elle ne mentionne chaque champ qu’une fois, et le code de l’application n’a aucune chance de mélanger accidentellement les index i
et j
car il ne les traite pas .
J'ai conçu cette API dans l'esprit de Comparator.comparing de Java.
Le code d'infrastructure pour le trieur ci-dessus est:
type Sorter struct{ keys []Key }
func NewSorter() *Sorter { return new(Sorter) }
func (l *Sorter) AddStr(key StringKey) *Sorter { l.keys = append(l.keys, key); return l }
func (l *Sorter) AddInt(key IntKey) *Sorter { l.keys = append(l.keys, key); return l }
func (l *Sorter) SortStable(slice interface{}) {
value := reflect.ValueOf(slice)
sort.SliceStable(slice, func(i, j int) bool {
si := value.Index(i).Interface()
sj := value.Index(j).Interface()
for _, key := range l.keys {
if key.Less(si, sj) {
return true
}
if key.Less(sj, si) {
return false
}
}
return false
})
}
type Key interface {
Less(a, b interface{}) bool
}
type StringKey func(interface{}) string
func (k StringKey) Less(a, b interface{}) bool { return k(a) < k(b) }
type IntKey func(interface{}) int
func (k IntKey) Less(a, b interface{}) bool { return k(a) < k(b) }
Cela a été très utile. Je devais trier une tranche de structs et j'ai trouvé ma réponse ici. En fait, je l'ai étendu au triplement. Bien que ce tri ne soit pas optimal pour l'exécution, il est utile dans certaines circonstances, notamment lorsque la solution de remplacement conduit à un code difficile à gérer ou à modifier et lorsque des exécutions plus rapides ne sont pas essentielles.
sort.Slice(servers, func(i, j int) bool {
if servers[i].code < servers[j].code {
return true
} else if servers[i].code > servers[j].code {
return false
} else {
if servers[i].group < servers[j].group {
return true
} else {
if servers[i].group > servers[j].group {
return false
}
}
}
return servers[i].IDGroup < servers[j]. IDGroup
})
Ce code trie d'abord par code, puis par groupe, puis par IDGroup.