sort.Interface
Part of the Object Oriented Programming section of Coddy's GO journey — lesson 74 of 107.
The sort package provides another excellent example of interface-based design. To sort a custom collection, your type must implement the sort.Interface:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}These three methods give the sort algorithm everything it needs: the collection's length, a way to compare elements, and a way to swap them. Here's how to make a slice of custom structs sortable:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Carol", 35},
}
sort.Sort(ByAge(people))
fmt.Println(people)
// [{Bob 25} {Alice 30} {Carol 35}]
}The key insight is creating a named type (ByAge) based on your slice. This lets you define different sorting behaviors for the same data. You could create ByName with a different Less implementation to sort alphabetically instead.
Once your type satisfies sort.Interface, it works with sort.Sort(), sort.Reverse(), and sort.IsSorted() automatically.
Challenge
EasyLet's build a sortable product inventory system that demonstrates the power of Go's sort.Interface! You'll create a Product type and implement multiple sorting strategies, allowing the same collection to be sorted in different ways.
You'll organize your code across two files:
product.go: Define your product type and sorting implementations.Create a
Productstruct with three fields:Name(string),Price(float64), andQuantity(int).Create two named types based on
[]Product:ByPrice- for sorting products by price in ascending orderByQuantity- for sorting products by quantity in descending order (highest quantity first)
Each type needs to implement the three methods required by
sort.Interface:Len(),Less(i, j int), andSwap(i, j int). TheLessmethod determines the sort order for each type.main.go: Build and sort your product inventory.Read a sort mode (
priceorquantity), then read a count followed by product details. Each product is provided as three lines: name, price, and quantity.Create a slice of products, sort them using the appropriate sorting type based on the mode, then print each product in this format:
[Name]: $[Price] (x[Quantity])Display prices with two decimal places.
The following inputs will be provided:
- Line 1: Sort mode (
priceorquantity) - Line 2: Number of products
- Following lines: Product details (name, price, quantity - three lines per product)
For example, given:
price
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Your output should be:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)And given:
quantity
3
Laptop
999.99
5
Mouse
29.99
50
Keyboard
79.99
25Your output should be:
Mouse: $29.99 (x50)
Keyboard: $79.99 (x25)
Laptop: $999.99 (x5)Notice how the same product data can be sorted differently by simply using a different named type. Once your types satisfy sort.Interface, they work seamlessly with sort.Sort() from the standard library.
Cheat sheet
The sort package requires types to implement the sort.Interface to be sortable:
type Interface interface {
Len() int
Less(i, j int) bool
Swap(i, j int)
}To make a custom slice sortable, create a named type and implement the three required methods:
type Person struct {
Name string
Age int
}
type ByAge []Person
func (a ByAge) Len() int { return len(a) }
func (a ByAge) Less(i, j int) bool { return a[i].Age < a[j].Age }
func (a ByAge) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
// Sort the collection
sort.Sort(ByAge(people))You can create multiple named types for the same slice to implement different sorting behaviors. Each type defines its own Less method to determine sort order.
Once a type satisfies sort.Interface, it works with sort.Sort(), sort.Reverse(), and sort.IsSorted().
Try it yourself
package main
import (
"bufio"
"fmt"
"os"
"sort"
"strconv"
)
func main() {
reader := bufio.NewReader(os.Stdin)
// Read sort mode
var mode string
fmt.Fscanln(reader, &mode)
// Read number of products
var count int
fmt.Fscanln(reader, &count)
// Read products
products := make([]Product, count)
for i := 0; i < count; i++ {
name, _ := reader.ReadString('\n')
name = name[:len(name)-1] // Remove newline
priceStr, _ := reader.ReadString('\n')
priceStr = priceStr[:len(priceStr)-1]
price, _ := strconv.ParseFloat(priceStr, 64)
qtyStr, _ := reader.ReadString('\n')
qtyStr = qtyStr[:len(qtyStr)-1]
quantity, _ := strconv.Atoi(qtyStr)
products[i] = Product{Name: name, Price: price, Quantity: quantity}
}
// TODO: Sort products based on mode
// If mode is "price", use ByPrice type
// If mode is "quantity", use ByQuantity type
// Use sort.Sort() with the appropriate type
// TODO: Print each product in the format:
// [Name]: $[Price] (x[Quantity])
// Use fmt.Printf with %.2f for price formatting
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of Go OOP
External FilesGo Workspace & ModulesPackages & ImportsExported vs Unexported NamesIntroduction to OOP in GoStructs as ClassesDefining Methods on StructsPointer vs Value ReceiversStruct InitializationConstructor FunctionsRecap - Simple Calculator4Interfaces
Introduction to InterfacesImplicit ImplementationInterface as ContractEmpty Interface (any)Type AssertionType SwitchInterface CompositionStringer & Error InterfacesRecap - Shape Calculator7Encapsulation
Exported vs Unexported FieldsPackage-Level EncapsulationGetter & Setter MethodsInformation Hiding in GoRecap - Student Records10Generics (Go 1.18+)
Introduction to GenericsType ParametersType ConstraintsGeneric StructsGeneric Methods WorkaroundRecap - Generic Collection2Types & Structs Deep Dive
Basic & Composite TypesCustom Type DefinitionsStruct TagsAnonymous StructsNested StructsZero Values & DefaultsRecap - Contact Book5Composition Over Inheritance
Why Go Has No InheritanceStruct Embedding BasicsMethod PromotionEmbedding Multiple StructsEmbedding vs AggregationShadowing Embedded MethodsRecap - Employee Hierarchy8Error Handling & OOP
The error InterfaceCustom Error TypesError Wrapping (fmt.Errorf)Sentinel Errorserrors.Is() and errors.As()Panic, Defer, and RecoverRecap - File Parser11Standard Library & OOP
io.Reader & io.Writersort.Interfacefmt.Stringer Interfaceencoding/json with Structshttp.Handler InterfaceRecap - REST API Models3Pointers & Memory
Pointer Basics in GoPointers to StructsPass by Value vs ReferenceThe new() FunctionGarbage Collection in GoRecap - Linked List Builder6Polymorphism in Go
Polymorphism via InterfacesDuck Typing in GoInterface Satisfaction RulesPolymorphic CollectionsDependency InjectionRecap - Payment Processor9Concurrency & OOP
Goroutines BasicsChannels & CommunicationBuffered vs Unbuffered ChanSelect Statementsync.Mutex & sync.RWMutexsync.WaitGroupThread-Safe Struct DesignRecap - Worker Pool