golang sort slice ascending or descending

I need to sort a slice of a type that is coming from a 3rdparty package. Based on some condition the order must be ascending or descending.

The solution I come up with is:

type fooAscending []foo

func (v fooAscending) Len() int           { return len(v) }
func (v fooAscending) Swap(i, j int)      { v[i], v[j] = v[j], v[i] }
func (v fooAscending) Less(i, j int) bool { return v[i].Amount < v[j].Amount }

type fooDescending []foo

func (v fooDescending) Len() int           { return len(v) }
func (v fooDescending) Swap(i, j int)      { v[i], v[j] = v[j], v[i] }
func (v fooDescending) Less(i, j int) bool { return v[i].Amount > v[j].Amount }

if someCondition {
    sort.Sort(fooAscending(array))
} else {
    sort.Sort(fooDescending(array))
}

Is there a better way to do this. 13 lines of code for this task and most of it is duplicated, seems a bit too much.


Solution 1:

As of Go 1.8, there is an easier way to sort a slice that does not require you to define new types. You simply pass an anonymous function to the sort.Slice function.

a := []int{5, 3, 4, 7, 8, 9}
sort.Slice(a, func(i, j int) bool {
    return a[i] < a[j]
})
for _, v := range a {
    fmt.Println(v)
}

This will sort in ascending order, if you want the opposite, simply write a[i] > a[j] in the anonymous function.

Solution 2:

You're looking for sort.Reverse. That will let you say:

sort.Sort(sort.Reverse(fooAscending(s)))

Solution 3:

My answer below is based on the assumption that the slice that you are receiving from a third party package is of a basic Go type.

To sort slices of basic types, use the sort package utilities. Here is an example that sorts a slice of string and a slice of int.

package main

import (
    "fmt"
    "sort"
)

func main() {
    sl := []string{"mumbai", "london", "tokyo", "seattle"}
    sort.Sort(sort.StringSlice(sl))
    fmt.Println(sl)

    intSlice := []int{3,5,6,4,2,293,-34}
    sort.Sort(sort.IntSlice(intSlice))
    fmt.Println(intSlice)
}

The output of the above is:

[london mumbai seattle tokyo]
[-34 2 3 4 5 6 293]

Go to Go Playground here to try it out yourself.

A few things of note:

  1. Sorting basic Go types does not require implementing functions such as Len() that belong to sort.Interface. You need to take that route only for composite types.

  2. Just wrap the type of a basic type using an appropriate Interface method provider, e.g. StringSlice, IntSlice, or Float64Slice, and sort.

  3. The slice is sorted in-place, and hence does not return a copy of the sorted slice.

Solution 4:

The accepted answer is good, but I disagree with their suggestion on descending:

a[i] > a[j]

With sort.Slice, the provided function is suppposed to represent an implementation of "less than":

func Slice(x interface{}, less func(i, j int) bool)

Slice sorts the slice x given the provided less function. It panics if x is not a slice.

So writing a "greater than" function, isnt really true to the given description. Better would be to reverse the indexes:

package main

import (
   "fmt"
   "sort"
)

func main() {
   a := []int{5, 3, 4, 7, 8, 9}
   sort.Slice(a, func(i, j int) bool {
      return a[j] < a[i]
   })
   fmt.Println(a) // [9 8 7 5 4 3]
}

both should return the same result, but I think one is more idiomatic.

https://golang.org/pkg/sort#Slice