The sort() method allows you to sort elements of an array in place. Besides returning the sorted array, the sort() method changes the positions of the elements in the original array. By default, the sort() method sorts the array elements in ascending order with the smallest value first and largest value last.
Sorting an array of objects in javascript is simple enough using the default sort() function for all arrays: const arr = [ { name: "Nina" }, { name: "Andre" }, { name: "Graham" } ]; const sortedArr = arr. sort((a,b) => { if ( a.name < b.name ){ return -1; } if ( a.name > b.name ){ return 1; } return 0; });
This time a and b are not numbers, but arrays. To sort the array of arrays, you need to specify based on which element you want to sort them. Here we compare the arrays by their second elements. Then the sort() function loops through the array of arrays and sorts it based on the magnitude of the second elements.
As of Go 1.8 you can now use sort.Slice to sort a slice:
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
There is normally no reason to use an array instead of a slice, but in your example you are using an array, so you have to overlay it with a slice (add [:]
) to make it work with sort.Slice
:
sort.Slice(planets[:], func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
The sorting changes the array, so if you really want you can continue to use the array instead of the slice after the sorting.
UPDATE: This answer relates to older versions of go
. For Go 1.8 and newer, see the AndreKR's answer above.
If you want something a bit less verbose than the standard library sort
package, you could use the third party github.com/bradfitz/slice
package. It uses some tricks to generate the Len
and Swap
methods needed to sort your slice, so you only need to provide a Less
method.
With this package, you can perform the sort with:
slice.Sort(planets[:], func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
The planets[:]
part is necessary to produce a slice covering your array. If you make planets
a slice instead of an array you could skip that part.
As of Go 1.8, @AndreKR's answer is the better solution.
You can implement a collection type which implements the sort interface.
Here's an example of two such types which allow you to sort either by Axis or 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)
}
You can, instead of implementing the Sort interface
on []Planet
you implement on a type that contains the collection and a closure that will do the comparison. You have to provide the implementation for the comparison closure for each property.
This method I feel is better than implementing a Sort type for each property of the struct.
This answer is almost ripped right from the sort docs so I can't take to much credit for it
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])
}
How to call it.
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)
}
Here is a Demo
Here is another way to reduce some of the boiler plate. Disclaimer, it uses reflection and losses type safety.
Here is a Demo
All the magic happens in the Prop
function. It takes the struct property to sort on and the order it which you want to sort (ascending, descending) and returns a function that will perform the comparisons.
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}
}
You can implement using quick sort as well and inside the partition func, you choose which field to sort by, I choose Name for example.
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
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With