我遇到了一个问题,我有一个结构数组,例如
package main
import "log"
type Planet struct {
Name string `json:"name"`
Aphelion float64 `json:"aphelion"` // in million km
Perihelion float64 `json:"perihelion"` // in million km
Axis int64 `json:"Axis"` // in km
Radius float64 `json:"radius"`
}
func main() {
var mars = new(Planet)
mars.Name = "Mars"
mars.Aphelion = 249.2
mars.Perihelion = 206.7
mars.Axis = 227939100
mars.Radius = 3389.5
var earth = new(Planet)
earth.Name = "Earth"
earth.Aphelion = 151.930
earth.Perihelion = 147.095
earth.Axis = 149598261
earth.Radius = 6371.0
var venus = new(Planet)
venus.Name = "Venus"
venus.Aphelion = 108.939
venus.Perihelion = 107.477
venus.Axis = 108208000
venus.Radius = 6051.8
planets := [...]Planet{*mars, *venus, *earth}
log.Println(planets)
}
假设您想按Axis
对其进行排序。你是怎么做到的?
(注意:我看过http://golang.org/pkg/sort/它似乎有用,但我必须添加大约20行才能通过一个非常简单的键进行简单排序。我有一个python背景,它就像{一样简单{1}} - 在Go中有类似的简单吗?)
答案 0 :(得分:194)
从Go 1.8开始,您现在可以使用sort.Slice对切片进行排序:
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
通常没有理由使用数组而不是切片,但在您的示例中, 使用数组,因此您必须使用切片覆盖它(添加[:]
)使它与sort.Slice
:
sort.Slice(planets[:], func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
排序会更改数组,因此如果您真的想要,可以在排序后继续使用数组而不是切片。
答案 1 :(得分:44)
更新:此答案与较早版本的go
有关。对于Go 1.8及更新版本,请参阅AndreKR's answer below。
如果您想要比标准库sort
包更简洁的东西,您可以使用第三方github.com/bradfitz/slice
包。它使用一些技巧来生成排序切片所需的Len
和Swap
方法,因此您只需提供Less
方法。
使用此软件包,您可以使用以下命令执行排序:
slice.Sort(planets[:], func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
生成覆盖阵列的切片需要planets[:]
部分。如果您使planets
切片而不是数组,则可以跳过该部分。
答案 2 :(得分:29)
从Go 1.8开始,@ AndreKR&#39; answer是更好的解决方案。
您可以实现实现sort interface。
的集合类型Here's an example两种类型允许您按轴或名称排序:
package main
import "log"
import "sort"
// AxisSorter sorts planets by axis.
type AxisSorter []Planet
func (a AxisSorter) Len() int { return len(a) }
func (a AxisSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a AxisSorter) Less(i, j int) bool { return a[i].Axis < a[j].Axis }
// NameSorter sorts planets by name.
type NameSorter []Planet
func (a NameSorter) Len() int { return len(a) }
func (a NameSorter) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a NameSorter) Less(i, j int) bool { return a[i].Name < a[j].Name }
type Planet struct {
Name string `json:"name"`
Aphelion float64 `json:"aphelion"` // in million km
Perihelion float64 `json:"perihelion"` // in million km
Axis int64 `json:"Axis"` // in km
Radius float64 `json:"radius"`
}
func main() {
var mars Planet
mars.Name = "Mars"
mars.Aphelion = 249.2
mars.Perihelion = 206.7
mars.Axis = 227939100
mars.Radius = 3389.5
var earth Planet
earth.Name = "Earth"
earth.Aphelion = 151.930
earth.Perihelion = 147.095
earth.Axis = 149598261
earth.Radius = 6371.0
var venus Planet
venus.Name = "Venus"
venus.Aphelion = 108.939
venus.Perihelion = 107.477
venus.Axis = 108208000
venus.Radius = 6051.8
planets := []Planet{mars, venus, earth}
log.Println("unsorted:", planets)
sort.Sort(AxisSorter(planets))
log.Println("by axis:", planets)
sort.Sort(NameSorter(planets))
log.Println("by name:", planets)
}
答案 3 :(得分:4)
您可以,而不是在Sort interface
上实现[]Planet
,而不是在包含集合的类型和将进行比较的闭包上实现。您必须为每个属性提供比较闭包的实现。
我觉得这个方法比为struct的每个属性实现Sort类型要好。
这个答案几乎是从sort docs直接撕掉的,所以我不能为此付出太多的荣誉
package main
import (
"log"
"sort"
)
type Planet struct {
Name string `json:"name"`
Aphelion float64 `json:"aphelion"` // in million km
Perihelion float64 `json:"perihelion"` // in million km
Axis int64 `json:"Axis"` // in km
Radius float64 `json:"radius"`
}
type By func(p1, p2 *Planet) bool
func (by By) Sort(planets []Planet) {
ps := &planetSorter{
planets: planets,
by: by,
}
sort.Sort(ps)
}
type planetSorter struct {
planets []Planet
by func(p1, p2 *Planet) bool
}
func (s *planetSorter) Len() int {
return len(s.planets)
}
func (s *planetSorter) Swap(i, j int) {
s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
}
func (s *planetSorter) Less(i, j int) bool {
return s.by(&s.planets[i], &s.planets[j])
}
如何调用它。
func main() {
/* Same code as in the question */
planets := []Planet{*mars, *venus, *earth}
By(func(p1, p2 *Planet) bool {
return p1.Name < p2.Name
}).Sort(planets)
log.Println(planets)
By(func(p1, p2 *Planet) bool {
return p1.Axis < p2.Axis
}).Sort(planets)
log.Println(planets)
}
答案 4 :(得分:3)
这是另一种减少锅炉板的方法。免责声明,它使用反射和损失类型安全。
所有的魔法都发生在Prop
函数中。它需要对struct属性进行排序以及对其进行排序(升序,降序)的顺序,并返回将执行比较的函数。
package main
import (
"log"
"reflect"
"sort"
)
func test(planets []Planet) {
log.Println("Sort Name")
By(Prop("Name", true)).Sort(planets)
log.Println(planets)
log.Println("Sort Aphelion")
By(Prop("Aphelion", true)).Sort(planets)
log.Println(planets)
log.Println("Sort Perihelion")
By(Prop("Perihelion", true)).Sort(planets)
log.Println(planets)
log.Println("Sort Axis")
By(Prop("Axis", true)).Sort(planets)
log.Println(planets)
log.Println("Sort Radius")
By(Prop("Radius", true)).Sort(planets)
log.Println(planets)
}
func Prop(field string, asc bool) func(p1, p2 *Planet) bool {
return func(p1, p2 *Planet) bool {
v1 := reflect.Indirect(reflect.ValueOf(p1)).FieldByName(field)
v2 := reflect.Indirect(reflect.ValueOf(p2)).FieldByName(field)
ret := false
switch v1.Kind() {
case reflect.Int64:
ret = int64(v1.Int()) < int64(v2.Int())
case reflect.Float64:
ret = float64(v1.Float()) < float64(v2.Float())
case reflect.String:
ret = string(v1.String()) < string(v2.String())
}
if asc {
return ret
}
return !ret
}
}
type Planet struct {
Name string `json:"name"`
Aphelion float64 `json:"aphelion"` // in million km
Perihelion float64 `json:"perihelion"` // in million km
Axis int64 `json:"Axis"` // in km
Radius float64 `json:"radius"`
}
type By func(p1, p2 *Planet) bool
func (by By) Sort(planets []Planet) {
ps := &planetSorter{
planets: planets,
by: by, // The Sort method's receiver is the function (closure) that defines the sort order.
}
sort.Sort(ps)
}
type planetSorter struct {
planets []Planet
by func(p1, p2 *Planet) bool // Closure used in the Less method.
}
// Len is part of sort.Interface.
func (s *planetSorter) Len() int { return len(s.planets) }
// Swap is part of sort.Interface.
func (s *planetSorter) Swap(i, j int) {
s.planets[i], s.planets[j] = s.planets[j], s.planets[i]
}
// Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.
func (s *planetSorter) Less(i, j int) bool {
return s.by(&s.planets[i], &s.planets[j])
}
func main() {
test(dataSet())
}
func dataSet() []Planet {
var mars = new(Planet)
mars.Name = "Mars"
mars.Aphelion = 249.2
mars.Perihelion = 206.7
mars.Axis = 227939100
mars.Radius = 3389.5
var earth = new(Planet)
earth.Name = "Earth"
earth.Aphelion = 151.930
earth.Perihelion = 147.095
earth.Axis = 149598261
earth.Radius = 6371.0
var venus = new(Planet)
venus.Name = "Venus"
venus.Aphelion = 108.939
venus.Perihelion = 107.477
venus.Axis = 108208000
venus.Radius = 6051.8
return []Planet{*mars, *venus, *earth}
}
答案 5 :(得分:0)
您还可以使用快速排序来实现,并且可以在分区函数内选择要排序的字段,例如,我选择“名称”。
package main
import (
"fmt"
)
type Planet struct {
Name string `json:"name"`
Aphelion float64 `json:"aphelion"` // in million km
Perihelion float64 `json:"perihelion"` // in million km
Axis int64 `json:"Axis"` // in km
Radius float64 `json:"radius"`
}
func main() {
var mars Planet
mars.Name = "Mars"
mars.Aphelion = 249.2
mars.Perihelion = 206.7
mars.Axis = 227939100
mars.Radius = 3389.5
var earth Planet
earth.Name = "Earth"
earth.Aphelion = 151.930
earth.Perihelion = 147.095
earth.Axis = 149598261
earth.Radius = 6371.0
var venus Planet
venus.Name = "Venus"
venus.Aphelion = 108.939
venus.Perihelion = 107.477
venus.Axis = 108208000
venus.Radius = 6051.8
planets := []Planet{mars, venus, earth}
fmt.Println(quickSort(&planets,0,len(planets)-1))
}
func quickSort(arr *[]Planet, start, end int)[]Planet{
if start < end{
partitionIndex := partition(*arr,start,end)
quickSort(arr,start,partitionIndex-1)
quickSort(arr,partitionIndex+1, end)
}
return *arr
}
func partition(arr []Planet, start, end int) int{
pivot := arr[end].Name
pIndex := start
for i:= start; i<end; i++{
if arr[i].Name <= pivot{
// swap
arr[i],arr[pIndex] = arr[pIndex],arr[i]
pIndex++
}
}
arr[pIndex],arr[end] = arr[end],arr[pIndex]
return pIndex
}