Question
I have a collection of structs in Go, for example:
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() {
mars := Planet{
Name: "Mars",
Aphelion: 249.2,
Perihelion: 206.7,
Axis: 227939100,
Radius: 3389.5,
}
earth := Planet{
Name: "Earth",
Aphelion: 151.930,
Perihelion: 147.095,
Axis: 149598261,
Radius: 6371.0,
}
venus := Planet{
Name: "Venus",
Aphelion: 108.939,
Perihelion: 107.477,
Axis: 108208000,
Radius: 6051.8,
}
planets := []Planet{mars, venus, earth}
log.Println(planets)
}
How can I sort this slice by a field such as Axis?
I found Go's sort package, but some approaches seem verbose compared with Python's sorted(planets, key=lambda p: p.Axis). Is there a shorter and idiomatic way in Go to sort a slice of structs by a chosen field?
Short Answer
By the end of this page, you will understand how sorting structs works in Go, why Go uses comparison functions instead of a key= parameter like Python, and the simplest idiomatic way to sort a slice of structs by fields such as Axis, Name, or Radius. You will also learn how in-place sorting works, how to sort ascending or descending, and what mistakes beginners often make.
Concept
In Go, sorting is usually done by telling the language how to compare two elements.
For a slice of structs, Go does not automatically know which field you want to sort by. A struct like Planet has multiple fields, so you must provide the sorting rule.
The most common modern approach is to use sort.Slice, which lets you pass a slice and a comparison function:
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
This means:
- compare the element at index
iwith the element at indexj - return
trueif elementishould come before elementj
This approach matters because real programs often need custom sorting:
- sort users by age
- sort products by price
- sort API results by timestamp
- sort records alphabetically by name
Unlike Python's key= style, Go prefers explicit comparison logic. That can feel slightly more verbose at first, but it is flexible and easy to read once you get used to it.
Mental Model
Think of sorting in Go like giving instructions to a librarian.
You hand the librarian a pile of books and say:
- "Put the book with the smaller page count first"
- or "Put the book with the earlier publication date first"
Go's sort.Slice works the same way. It keeps asking your comparison function:
- "Should item
icome before itemj?"
Your function answers with true or false.
So instead of saying extract this key, you say compare these two items using this field.
Syntax and Examples
The basic syntax for sorting a slice of structs is:
sort.Slice(sliceName, func(i, j int) bool {
return sliceName[i].Field < sliceName[j].Field
})
Example: sort by Axis
package main
import (
"fmt"
"sort"
)
type Planet struct {
Name string
Axis int64
}
func main() {
planets := []Planet{
{Name: "Mars", Axis: 227939100},
{Name: "Venus", Axis: 108208000},
{Name: "Earth", Axis: 149598261},
}
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
fmt.Println(planets)
}
Output order:
[{Venus 108208000} {Earth 149598261} {Mars }]
Step by Step Execution
Consider this example:
planets := []Planet{
{Name: "Mars", Axis: 227939100},
{Name: "Venus", Axis: 108208000},
{Name: "Earth", Axis: 149598261},
}
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
Here is what happens step by step:
-
sort.Slicereceives theplanetsslice. -
Go starts comparing elements at different indexes.
-
Suppose it compares
MarsandVenus. -
The function checks:
planets[i].Axis < planets[j].Axis -
For
Mars(227939100) andVenus(108208000), the result isfalse.
Real World Use Cases
Sorting structs by a field is common in everyday Go programs.
API responses
You might fetch data from a database or external API and sort it before returning JSON:
- users by signup date
- orders by total amount
- blog posts by publish time
CLI tools
A command-line tool might sort:
- files by size
- processes by memory usage
- tasks by priority
Dashboards and admin panels
Backend code often sorts records before rendering or sending them to the frontend:
- products by price
- employees by last name
- planets by radius or distance
Data processing
When analyzing data, sorting helps with:
- ranking results
- finding smallest/largest items
- preparing reports in a predictable order
Real Codebase Usage
In real Go codebases, developers often use sorting in a few standard patterns.
1. Inline sorting with sort.Slice
This is the most common choice for one-off sorts:
sort.Slice(users, func(i, j int) bool {
return users[i].CreatedAt.Before(users[j].CreatedAt)
})
Good when the sort rule is simple and used only once.
2. Reusable helper functions
If you sort by the same field in many places, create a helper:
func SortPlanetsByAxis(planets []Planet) {
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
}
This improves readability and reuse.
3. Guard clauses before sorting
Developers may skip unnecessary work:
if len(planets) < 2 {
return
}
4. Multi-field sorting
If two values are equal, compare another field:
Common Mistakes
1. Expecting a new slice to be returned
This is a common mistake for people coming from Python.
Broken expectation:
sortedPlanets := sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
Problem:
sort.Slicedoes not return the sorted slice- it modifies the original slice in place
Correct usage:
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
2. Using an array instead of a slice without thinking about it
The original example used an array-like form:
planets := [...]Planet{mars, venus, earth}
This creates an array, not a slice. While slices are the most common thing to sort in Go.
Prefer:
planets := []Planet{mars, venus, earth}
3. Writing inconsistent comparison logic
Broken example:
Comparisons
| Approach | How it works | Best for | Notes |
|---|---|---|---|
sort.Slice | Pass a slice and comparison function | Most struct sorting in modern Go | Short and idiomatic |
sort.SliceStable | Like sort.Slice, but keeps equal items in original order | When stability matters | Slightly different behavior |
Custom sort.Interface | Define Len, Less, and Swap methods | Reusable named sort types | More verbose |
Python sorted(..., key=...) | Pass a key extraction function |
Cheat Sheet
import "sort"
Sort a slice of structs ascending
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
Sort descending
sort.Slice(planets, func(i, j int) bool {
return planets[i].Axis > planets[j].Axis
})
Stable sort
sort.SliceStable(planets, func(i, j int) bool {
return planets[i].Axis < planets[j].Axis
})
Sort by string field
sort.Slice(planets, func(i, j int) bool {
return planets[i].Name < planets[j].Name
})
Multi-field sort
FAQ
Is there a Python-like key= argument in Go?
Not in the same form. In Go, the normal pattern is to provide a comparison function with sort.Slice.
What is the simplest way to sort a slice of structs in Go?
Use sort.Slice with a small comparison function.
sort.Slice(items, func(i, j int) bool {
return items[i].Field < items[j].Field
})
Does sort.Slice return a new slice?
No. It sorts the existing slice in place.
Can I sort by multiple fields?
Yes. Compare the primary field first, and if equal, compare a secondary field.
Should I use sort.Slice or sort.Interface?
Use sort.Slice for most cases. Use sort.Interface when you want a reusable named sorting type.
How do I sort in descending order?
Reverse the comparison:
items[i].Field > items[j].Field
Mini Project
Description
Build a small Go program that stores planets in a slice and lets you sort them by different fields. This demonstrates how to sort structs by numeric and string fields using idiomatic Go.
Goal
Create a program that sorts a slice of Planet structs by Axis, Radius, and Name, then prints the results.
Requirements
Define a Planet struct with at least Name, Axis, and Radius fields.
Create a slice containing at least four planets.
Sort the slice by Axis in ascending order.
Sort the slice by Radius in descending order.
Sort the slice by Name alphabetically and print each result.
Keep learning
Related questions
Automatic Build Versioning in Go: Embed Incrementing Build Numbers
Learn how to add automatic build versioning in Go using linker flags, build metadata, CI counters, and Git-based version values.
Blank Identifier Imports in Go: What `_` Means in an Import Statement
Learn what `_` means in a Go import, why blank identifier imports run package init code, and when to use them safely.
Calling Functions Across Files in the Same Go Package
Learn how Go uses packages across multiple files, why functions may appear undefined, and how to organize code correctly.