[UDPATE]我的错.我应该更彻底地阅读文档,而不是立即提出这个问题.我的错.
我有一个数组/片成员:
type Member struct {
Id int
LastName string
FirstName string
}
var members []Member
Run Code Online (Sandbox Code Playgroud)
我的问题是如何将它们排序LastName,然后按FirstName.
任何帮助表示赞赏谢谢.
Cer*_*món 46
使用sort.Slice(自Go 1.8起可用)或sort.Sort函数对一片值进行排序.
使用这两个函数,应用程序提供了一个函数,用于测试一个slice元素是否小于另一个slice元素.要按姓氏和名字排序,请比较姓氏和名字:
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
Run Code Online (Sandbox Code Playgroud)
使用sort.Slice的匿名函数指定less函数:
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
})
Run Code Online (Sandbox Code Playgroud)
通过具有sort.Sort函数的接口指定less函数:
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))
Run Code Online (Sandbox Code Playgroud)
除非性能分析显示排序是一个热点,否则请使用对您的应用程序最方便的功能.
abo*_*get 17
使用更新的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
})
Run Code Online (Sandbox Code Playgroud)
或类似的东西.
我为此编写的最短但仍然易于理解的代码是:
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
}
})
}
Run Code Online (Sandbox Code Playgroud)
使用该switch语句的模式很容易扩展到两个以上的排序标准,并且仍然足够短以供阅读。
这是程序的其余部分:
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)
}
}
Run Code Online (Sandbox Code Playgroud)
如果你想避免多次提到字段LastName和FirstName,如果你想避免混合i和j(这可能一直发生),我玩了一下,基本思想是:
func sortByLastNameAndFirstNameFunctional(members []Member) {
NewSorter().
AddStr(member -> member.LastName).
AddStr(member -> member.FirstName).
AddInt(member -> member.Id).
SortStable(members)
}
Run Code Online (Sandbox Code Playgroud)
由于 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)
}
Run Code Online (Sandbox Code Playgroud)
实现以及 API 的使用interface{}和反射有点丑陋,但它只提到每个字段一次,并且应用程序代码没有机会意外混合索引i,j因为它不处理它们。
我本着 Java 的Comparator.comparing的精神设计了这个 API 。
上述分拣机的基础设施代码是:
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) }
Run Code Online (Sandbox Code Playgroud)
我发现另一种更干净的模式:
if members[i].LastName != members[j].LastName {
return members[i].LastName < members[j].LastName
}
return members[i].FirstName < members[j].FirstName
Run Code Online (Sandbox Code Playgroud)
比当前接受的答案稍微干净的解决方案是做类似这样的事情:
sort.Slice(members, func(i, j int) bool {
if members[i].FirstName != members[j].FirstName {
return members[i].FirstName < members[j].FirstName
}
return members[i].LastName < members[j].LastName
})
Run Code Online (Sandbox Code Playgroud)
这样做的好处是,如果添加新字段Age(或其他内容),您将更清楚如何扩展它:
sort.Slice(members, func(i, j int) bool {
if members[i].FirstName != members[j].FirstName {
return members[i].FirstName < members[j].FirstName
}
if members[i].Age != members[j].Age {
return members[i].Age < members[j].Age
}
return members[i].LastName < members[j].LastName
})
Run Code Online (Sandbox Code Playgroud)
因此,模式是if X[i] != X[j]为所有属性添加一条语句,直到最后一个属性,这只是正常比较。