Represent and transform data safely
Find the Maximum in a Go Slice Without Sorting It
Find the maximum in a Go slice without sorting it. Use a loop for an explicit empty-input result or slices.Max for non-empty ordered values.

If you need only the largest value in a slice, leave the slice in its original order. A loop can track the maximum as it reads each element. For a non-empty slice of ordered values, slices.Max is a shorter option. The decision to make first is what your function should do when there is no value to return.
Give empty input its own result
An int function that returns 0 for an empty slice leaves callers with an ambiguity: was the slice empty, or was its maximum actually zero? Return (value, ok) when an empty slice is an expected possibility. Set ok to false for empty input; callers should inspect it before using value.
If empty input instead means the caller has made a mistake, an (int, error) return is a clearer contract. That lets the function explain why it could not produce a maximum. Choose the contract at the function boundary rather than making every caller guess what a zero means.
Find the maximum in one pass
Check for an empty slice before reading its first element. Then initialize the maximum from that element—not from zero—and compare each remaining value against it:
package main
import "fmt"
func maxInt(values []int) (int, bool) {
if len(values) == 0 {
return 0, false
}
max := values[0]
for _, value := range values[1:] {
if value > max {
max = value
}
}
return max, true
}
func main() {
for _, values := range [][]int{
{},
{7},
{-9, -3, -14},
{0, 6, 2},
} {
value, ok := maxInt(values)
fmt.Printf("%v: value=%d ok=%t\n", values, value, ok)
}
}
For the empty slice, the returned value is 0, but ok is false: that zero is a placeholder, not a maximum. The singleton returns 7. For {-9, -3, -14}, the result is -3, because it is greater than the other two values. Initializing max to zero would get that case wrong. The loop neither sorts nor modifies the input; it keeps just the best value seen so far.
This helper is a good fit when its empty-input policy is part of an API used by several callers. It also makes that policy visible in the return type. At a call site, handle both results explicitly:
value, ok := maxInt(values)
if !ok {
// Handle the absence of a maximum here.
} else {
// Use value here.
}
The ok result matters even if most calls have values. A caller that ignores it can still mistake the placeholder zero for a maximum. Conversely, when ok is true, zero is an ordinary possible result; the flag reports whether a maximum was found, not whether that maximum is nonzero.
Use slices.Max for non-empty ordered slices
The standard library’s slices.Max accepts a slice whose element type satisfies cmp.Ordered and returns an element of that type. If your code must represent absence explicitly, check the slice before calling it:
package main
import (
"fmt"
"slices"
)
func main() {
values := []int{-9, -3, -14}
if len(values) == 0 {
fmt.Println("no maximum")
return
}
fmt.Println(slices.Max(values)) // -3
}
For this non-empty slice, slices.Max and maxInt select the same value, -3. Use slices.Max when the caller already knows the slice is non-empty or can handle that check nearby. Keep the loop helper when you want one function to own the (value, ok) contract. Neither approach needs to rearrange the slice to find its largest value.





Comments
No comments yet.