我只是有一个问题,我有一个数组的结构体,例如。

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背景,它就像排序一样简单(行星,键=lambda n: n. axis) -在Go中有类似的简单吗?)


当前回答

从Go 1.8开始,你就可以使用排序了。Slice对切片进行排序:

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

通常没有理由使用数组而不是切片,但在您的示例中使用的是数组,因此必须使用切片(add[:])覆盖它以使其与排序一起工作。片:

sort.Slice(planets[:], func(i, j int) bool {
  return planets[i].Axis < planets[j].Axis
})

排序会改变数组,所以如果你真的想,你可以在排序后继续使用数组而不是切片。

其他回答

至于Go 1.8, @AndreKR的答案是更好的解决方案。


您可以实现一个实现排序接口的集合类型。

下面是两个这样的类型的例子,它们允许你通过Axis或Name进行排序:

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)
}

你也可以使用快速排序实现,在分区func中,你选择哪个字段来排序,例如我选择Name。

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
}

您可以在[]Planet上实现Sort接口,而不是在包含集合和将进行比较的闭包的类型上实现Sort接口。您必须为每个属性的比较闭包提供实现。

我觉得这个方法比为结构的每个属性实现Sort类型更好。

这个答案几乎是直接从排序文档中撕下来的,所以我不能对此有太多的功劳

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)
}

这里是一个演示

从Go 1.8开始,你就可以使用排序了。Slice对切片进行排序:

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

通常没有理由使用数组而不是切片,但在您的示例中使用的是数组,因此必须使用切片(add[:])覆盖它以使其与排序一起工作。片:

sort.Slice(planets[:], func(i, j int) bool {
  return planets[i].Axis < planets[j].Axis
})

排序会改变数组,所以如果你真的想,你可以在排序后继续使用数组而不是切片。

更新:这个答案与旧版本的go有关。对于Go 1.8和更新版本,请参阅上面AndreKR的答案。


如果您想要一些比标准库排序包更简洁的包,您可以使用第三方的github.com/bradfitz/slice包。它使用了一些技巧来生成排序切片所需的Len和Swap方法,因此您只需要提供一个Less方法。

使用这个包,你可以执行排序:

slice.Sort(planets[:], func(i, j int) bool {
    return planets[i].Axis < planets[j].Axis
})

行星[:]部分是必要的,以产生一个切片覆盖你的阵列。如果你把行星做成一个切片而不是数组,你就可以跳过这个部分。