How to Sort Slice of Structs in Go
-
Sort Slice of Structs by a Field in GoLang Using the
sort.Slice
Function -
Sort Slice of Structs by a Field in GoLang Using the
sort.SliceStable
Method -
Sort Slice of Structs by Multiple Fields in GoLang Using the
sort.SliceStable
Method -
Sort Slice of Structs in GoLang Using the
sort.Interface
Methods - Conclusion
Sorting slices of structs is a common task in GoLang, and the language provides several methods to achieve this, each with its advantages and use cases. In this article, we will explore three prominent approaches for sorting slices of structs: using sort.Slice
, sort.SliceStable
, and sort.Interface
methods.
Sort Slice of Structs by a Field in GoLang Using the sort.Slice
Function
The sort
package provides a convenient method for sorting a slice of structs in GoLang based on a specific field - the sort.Slice
function. Along with sort.Slice
, a custom less
function can be used to define the sorting logic.
The sort.Slice
function provides a convenient way to sort slices of elements using a custom less function. The primary advantage of using sort.Slice
is its flexibility, as it allows you to sort slices of any type, not just slices of basic data types.
It has the following syntax:
func Slice(slice interface{}, less func(i, j int) bool)
Where:
slice
: The slice to be sorted. It should be a slice of a type for which theless
function is defined.less
: A function that takes two indices,i
andj
, and returns a Boolean indicating whether the element at indexi
should be considered less than the element at indexj
. This function defines the sorting order.
Let’s consider an example where we have a slice of Employee
structs, and we want to sort them based on the Salary
field. Here’s the complete working code:
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
func main() {
employees := []Employee{
{Name: "John", Salary: 1500},
{Name: "Joe", Salary: 3000},
{Name: "Jack", Salary: 3400},
}
sort.Slice(employees, func(i, j int) bool {
return employees[i].Salary < employees[j].Salary
})
fmt.Println("Sorted by Salary (Ascending):", employees)
}
In this example, we declare a custom struct named Employee
, which has two fields - Name
of type string and Salary
of type integer. This struct represents the individual employees with their names and corresponding salaries.
Moving on to the main
function, we initialize a slice of Employee
structs named employees
. This slice is a collection of three Employee
instances, each initialized with a name and a salary. This forms the dataset that we want to sort.
The key part of the code is the use of sort.Slice
. This function takes two arguments - the slice to be sorted (employees
in this case) and a custom less
function.
The less
function defines the sorting order based on the Salary
field. It takes two indices, i
and j
, representing two elements in the slice. The function compares the salaries of the employees at these indices and returns true
if the salary at index i
is less than the salary at index j
. This logic dictates the sorting order.
After applying the sort.Slice
method, the employees
slice is now sorted in ascending order based on the Salary
field. Finally, we use fmt.Println
to display the sorted slice, indicating that it is sorted by salary in ascending order.
Output:
Sorted by Salary (Ascending): [{John 1500} {Joe 3000} {Jack 3400}]
This indicates that the slice of Employee
structs has been successfully sorted in ascending order based on the Salary
field.
Sort Slice of Structs by a Field in GoLang Using the sort.SliceStable
Method
An alternative method for sorting a slice of structs in GoLang is the sort.SliceStable
method, along with a custom less
function.
Similar to the previous example, sort.SliceStable
also takes a slice (x
) and a custom less
function. It has the following syntax:
func SliceStable(slice interface{}, less func(i, j int) bool)
Where:
slice
: This parameter is the slice to be sorted. It should be an interface value that represents a slice.less
: This is a function that takes two indices,i
andj
, and returns a Boolean indicating whether the element at indexi
should be considered less than the element at indexj
. This function defines the custom sorting order.
The key distinction is that sort.SliceStable
guarantees a stable sort, meaning that when two elements have equal sorting keys, their original order in the slice is preserved. It is particularly useful when you want to achieve a stable sort, ensuring that the relative order of elements with equal keys remains unchanged after sorting.
Consider the same scenario where we have a slice of Employee
structs, and we want to sort them based on the Salary
field. Here’s the complete working code:
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
func main() {
employees := []Employee{
{Name: "John", Salary: 1500},
{Name: "Joe", Salary: 3000},
{Name: "Jack", Salary: 3400},
}
sort.SliceStable(employees, func(i, j int) bool {
return employees[i].Salary < employees[j].Salary
})
fmt.Println("Sorted by Salary (Stable):", employees)
}
This code largely resembles the previous example, with the primary distinction being the use of sort.SliceStable
instead of sort.Slice
. We continue to define the Employee
struct and initialize a slice of Employee
structs named employees
.
The sort.SliceStable
method is applied with the same custom less
function, which compares the Salary
values of two employees at indices i
and j
.
If the salary at index i
is less than the salary at index j
, the function returns true
, indicating that i
should come before j
in the sorted order. The stability of the sort is crucial; it ensures that when two employees have equal salaries, their original order in the slice is preserved.
As before, we utilize fmt.Println
to display the sorted slice, indicating that it is sorted by salary in a stable manner.
Output:
Sorted by Salary (Stable): [{John 1500} {Joe 3000} {Jack 3400}]
This output affirms that the slice of Employee
structs has been successfully sorted in ascending order based on the Salary
field while maintaining stability in the relative order of equal salaries.
Sort Slice of Structs by Multiple Fields in GoLang Using the sort.SliceStable
Method
Expanding on our exploration of sorting slices of structs in GoLang, we will now focus on sorting by multiple fields. This scenario often arises when you need to prioritize sorting based on one field and then, if there are ties, sort by another field.
In this section, we’ll utilize the sort.SliceStable
method with a custom less
function to achieve this multi-field sorting.
The process is analogous to sorting by a single field, but the custom less
function becomes more intricate. The function needs to compare multiple fields sequentially.
If the values of the first field are equal, it proceeds to compare the values of the second field, and so on. This ensures a stable sort by maintaining the order of equal elements based on the previous fields.
Let’s consider a scenario where we have a slice of Employee
structs, and we want to sort them first by Position
and then by Name
. Here’s the complete working code:
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Position string
}
func main() {
employees := []Employee{
{"Michael", "Developer"},
{"Jack", "Manager"},
{"Joe", "CEO"},
{"Leonard", "Intern"},
{"Sheldon", "Developer"},
}
sort.SliceStable(employees, func(i, j int) bool {
if employees[i].Position != employees[j].Position {
return employees[i].Position < employees[j].Position
}
return employees[i].Name < employees[j].Name
})
fmt.Println("Sorted by Position and then by Name (Stable):", employees)
}
In this code, the central data structure is the Employee
struct, characterized by two fields: Name
(a string representing the employee’s name) and Position
(a string indicating the employee’s position within the company).
The main
function initializes a slice of Employee
structs named employees
. This slice serves as the dataset that we aim to sort based on the specified fields.
The core logic revolves around the use of the sort.SliceStable
method, which ensures a stable sort. A custom less
function is provided as an argument to this method, dictating the sorting order.
This particular less
function compares the Position
values of two employees. If the positions are different, it returns true
if the position at index i
is less than the position at index j
. If the positions are equal, it proceeds to compare the Name
values, ensuring a stable sort by maintaining the order of equal positions based on the names.
Finally, the sorted slice is displayed using fmt.Println
.
This multi-field sorting provides a practical example of how to prioritize sorting based on one field and then, in the case of ties, sorting by another field.
Output:
Sorted by Position and then by Name (Stable): [{Joe CEO} {Michael Developer} {Sheldon Developer} {Leonard Intern} {Jack Manager}]
This output illustrates that the slice of Employee
structs has been successfully sorted first by Position
and then, for employees with the same position, by Name
. Understanding this approach allows you to effectively handle more complex sorting requirements in GoLang.
Sort Slice of Structs in GoLang Using the sort.Interface
Methods
The sort
package also provides a powerful mechanism for sorting slices of structs using the sort.Interface
interface. By implementing specific methods from this interface, we gain fine-grained control over the sorting process.
This approach offers flexibility and allows us to define custom sorting logic tailored to our struct’s requirements.
The sort.Interface
interface in GoLang requires the implementation of three methods: Len() int
, Less(i, j int) bool
, and Swap(i, j int)
.
Here is the syntax for each of these methods:
func (x YourType) Len() int {
// Return the length of the slice or collection.
}
The Len
method should return the number of elements in the collection.
func (x YourType) Less(i, j int) bool {
// Return true if the element at index i should be considered less than the element at index j.
}
The Less
method should define the custom comparison logic. It returns true
if the element at index i
should be considered less than the element at index j
; otherwise, it returns false
.
func (x YourType) Swap(i, j int) {
// Swap the elements at indices i and j.
}
The Swap
method should exchange the elements at indices i
and j
in the collection.
Let’s delve into a practical example by sorting a slice of Employee
structs based on the Salary
field:
package main
import (
"fmt"
"sort"
)
type Employee struct {
Name string
Salary int
}
type EmployeeSlice []Employee
func (e EmployeeSlice) Len() int {
return len(e)
}
func (e EmployeeSlice) Swap(i, j int) {
e[i], e[j] = e[j], e[i]
}
func (e EmployeeSlice) Less(i, j int) bool {
return e[i].Salary < e[j].Salary
}
func main() {
employees := EmployeeSlice{
{Name: "John", Salary: 1500},
{Name: "Joe", Salary: 3000},
{Name: "Jack", Salary: 3400},
}
sort.Sort(employees)
fmt.Println("Sorted by Salary:", employees)
}
In this example, we define a custom type, EmployeeSlice
, which is a slice of Employee
structs. We then implement the required methods for the sort.Interface
interface on this custom type.
The Len()
method returns the length of the slice, Swap(i, j int)
swaps elements at indices i
and j
, and Less(i, j int) bool
compares the Salary
values of the Employee
structs at indices i
and j
.
Moving on to the main
function, a slice of Employee
structs named employees
is initialized with a dataset representing employees and their respective salaries. The sort.Sort
function is then employed, taking advantage of the sort.Interface
methods implemented for the custom EmployeeSlice
type.
This initiates the sorting process, arranging the Employee
structs in ascending order based on their Salary
field. The result is a sorted slice of Employee
structs based on the Salary
field.
Output:
Sorted by Salary: [{John 1500} {Joe 3000} {Jack 3400}]
This output confirms that the slice of Employee
structs has been successfully sorted based on the Salary
field using the sort.Interface
methods. This approach provides a comprehensive way to handle custom sorting logic for slices of structs in GoLang programs.
Conclusion
Sorting slices of structs in GoLang can be accomplished using different methods, each suited to specific scenarios.
Whether you need a simple sort with sort.Slice
, stable sorting with sort.SliceStable
, or a more customized approach with sort.Interface
, GoLang provides the tools to efficiently manage and organize your data. Understanding these methods empowers you to choose the most suitable approach for your sorting requirements.
Sheeraz is a Doctorate fellow in Computer Science at Northwestern Polytechnical University, Xian, China. He has 7 years of Software Development experience in AI, Web, Database, and Desktop technologies. He writes tutorials in Java, PHP, Python, GoLang, R, etc., to help beginners learn the field of Computer Science.
LinkedIn FacebookRelated Article - Go Slice
- How to Delete an Element From a Slice in Golang
- How to Copy Slice in Go
- Difference Between []String and ...String in Go
- How to Check if a Slice Contains an Element in Golang
- How to Create an Empty Slice in Go