メンバーの配列/スライスがあります:
type Member struct {
Id int
LastName string
FirstName string
}
var members []Member
私の質問は、LastName
で並べ替えてからFirstName
で並べ替える方法です。
sort.Slice (Go 1.8以降で使用可能)または sort.Sort 関数を使用して、値のスライスをソートします。
両方の機能により、アプリケーションは、1つのスライス要素が別のスライス要素よりも小さいかどうかをテストする機能を提供します。姓、次に名で並べ替えるには、姓と名を比較します。
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
Less関数は、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
})
Less関数は、sort.Sort関数で interface を使用して指定されます。
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))
パフォーマンス分析でソートがホットスポットであることが示されていない限り、アプリケーションに最も便利な機能を使用してください。
新しいsort.Slice
関数を次のように使用します。
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
})
またはそのようなもの。
別のパターン、私はわずかにきれいだと思う:
if members[i].LastName != members[j].LastName {
return members[i].LastName < members[j].LastName
}
return members[i].FirstName < members[j].FirstName
このために私がなんとか書いた、最短でまだ理解可能なコードは次のとおりです。
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
}
})
}
switch
ステートメントを使用したパターンは、3つ以上のソート基準に簡単に拡張できますが、読み取り可能なほど短いものです。
プログラムの残りの部分は次のとおりです。
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)
}
}
LastName
フィールドとFirstName
フィールドに複数回言及するのを避けたい場合、およびi
とj
を混在させたくない場合(これは常に発生する可能性があります)、私は少し遊んで、基本的な考え方は次のとおりです:
func sortByLastNameAndFirstNameFunctional(members []Member) {
NewSorter().
AddStr(member -> member.LastName).
AddStr(member -> member.FirstName).
AddInt(member -> member.Id).
SortStable(members)
}
Goは、匿名関数を作成するための->
演算子をサポートせず、Javaなどのジェネリックも持たないため、構文上のオーバーヘッドが少し必要です。
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)
}
実装とAPIはinterface{}
とリフレクションを使用すると少しいですが、各フィールドに1回しか言及しておらず、アプリケーションコードはインデックスi
とj
を誤って混在させることはありません。対処しません。
このAPIは、Javaの Comparator.comparing の精神で設計しました。
上記のソーターのインフラストラクチャコードは次のとおりです。
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) }
受け入れられた答えのもう少し簡潔な実装を次に示します。
package main
import (
"fmt"
"sort"
)
type Member struct {
FirstName string
LastName string
}
func main() {
members := []Member{
{"John", "Doe"},
{"Jane", "Doe"},
{"Mary", "Contrary"},
}
fmt.Println(members)
sort.Slice(members, func(i, j int) bool {
return members[i].LastName < members[j].LastName || members[i].FirstName < members[j].FirstName
})
fmt.Println(members)
}
実行すると、次の出力が印刷されます。
[{John Doe} {Jane Doe} {Mary Contrary}]
[{Mary Contrary} {Jane Doe} {John Doe}]
これは予想どおりです。メンバーは姓の昇順で印刷され、同点の場合は名の順に印刷されます。
これは非常に役に立ちました。構造体のスライスを並べ替える必要があり、ここで答えを見つけました。実際に、3回ソートに拡張しました。これだけのソートはランタイムの目的には最適ではありませんが、状況によっては特に役立ちます。代替手段が、メンテナンスや変更が困難なコードにつながり、高速なランタイムが重要でない場合に特に役立ちます。
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
})
このコードは、最初にコードで、次にグループで、次にIDGroupでソートされます。