To find the median of a slice in Go, you need to write a custom function that sorts the input slice and gets the middle element. If the slice size is even, the function calculates the mean (average) of the two middle values. In the following examples, we will show two versions of this function: a regular one that takes a float64
slice as input, and a Generics version, that can work for any numeric type.
If you are not already familiar with the basics of Generics in Go, check out our series of articles: Introduction to Go Generics by example.
Also, check out our tutorial on how to calculate the arithmetic mean in Go.
Calculate median in Go - example
To calculate the median in the classic way, we need to create a new median()
function and start with the input data copy. Calculating the median requires sorting the data first, so to avoid modifying the original slice order, we need a copy of the input slice. We then take the middle value from this sorted slice. This is our median. If the data size is an odd number, the middle element will be the value at index <slice_length>/2
. If the data size is even, it will be the arithmetic average of the two middle elements.
package main
import (
"fmt"
"sort"
)
func median(data []float64) float64 {
dataCopy := make([]float64, len(data))
copy(dataCopy, data)
sort.Float64s(dataCopy)
var median float64
l := len(dataCopy)
if l == 0 {
return 0
} else if l%2 == 0 {
median = (dataCopy[l/2-1] + dataCopy[l/2]) / 2
} else {
median = dataCopy[l/2]
}
return median
}
func main() {
fmt.Println(median([]float64{1, 3, 2}))
fmt.Println(median([]float64{1}))
fmt.Println(median([]float64{3, 3, 3, 3, 2, 22, 2, 2, 2, 2, 1, 1, 1, 1, 1, 111}))
}
Output:
2
1
2
Use Generics to calculate median
Since the release of Generics in Go 1.18, it is possible to write the median()
function that works for a slice of any numeric type. Simply define the Number
constraint which is a union of constraints.Float
and constraints.Integer
to limit the input types to numeric. For more details, see the example on how to calculate the arithmetic mean using Generics, where we also used the Number
constraint.
Another difference is that, in the generic version, we use the slices.Sort()
function from the new /x/exp/slices
package to sort the slice. It allows for generic sorting of slices of any type.
package main
import (
"fmt"
"golang.org/x/exp/constraints"
"golang.org/x/exp/slices"
)
type Number interface {
constraints.Float | constraints.Integer
}
func median[T Number](data []T) float64 {
dataCopy := make([]T, len(data))
copy(dataCopy, data)
slices.Sort(dataCopy)
var median float64
l := len(dataCopy)
if l == 0 {
return 0
} else if l%2 == 0 {
median = float64((dataCopy[l/2-1] + dataCopy[l/2]) / 2.0)
} else {
median = float64(dataCopy[l/2])
}
return median
}
func main() {
fmt.Println(median([]float64{1, 3, 2}))
fmt.Println(median([]int{1}))
fmt.Println(median([]int32{3, 3, 3, 3, 2, 22, 2, 2, 2, 2, 1, 1, 1, 1, 1, 111}))
}
The result is a function that works seamlessly for a slice of type int
, int64
, float64
, etc., without having to copy code, or create special versions for each type.
Output:
2
1
2