Golang slices.SortStableFunc Function

The slices.SortStableFunc function in Golang is part of the slices package, introduced in Go 1.21 as part of the standard library. This function allows you to sort the elements of a slice in place using a custom comparison function, while maintaining the relative order of equal elements. This stable sorting behavior is particularly useful when the original order of equivalent elements matters.

Table of Contents

  1. Introduction
  2. slices.SortStableFunc Function Syntax
  3. Examples
    • Basic Usage
    • Sorting a Slice of Structs with Stable Sorting
    • Custom Stable Sorting for Complex Types
  4. Real-World Use Case Example
  5. Conclusion

Introduction

The slices.SortStableFunc function provides a stable sorting mechanism that preserves the relative order of elements that compare as equal. This is particularly useful in scenarios where the input data has a natural order that should be maintained for equal elements, such as when sorting by a primary key while keeping the original order of secondary keys.

slices.SortStableFunc Function Syntax

The syntax for the slices.SortStableFunc function is as follows:

func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int)

Parameters:

  • x S: The slice to be sorted.
  • cmp func(a, b E) int: A custom comparison function that defines the sorting order. It should return:
    • A negative number if a should come before b.
    • Zero if a and b are considered equal.
    • A positive number if a should come after b.

Returns:

  • void: This function does not return a value; it modifies the slice in place.

Behavior:

  • Stable in-place sorting: The function sorts the elements in the slice x in place according to the custom comparison logic defined by the cmp function, while preserving the relative order of equal elements.

Examples

Basic Usage

This example demonstrates how to use slices.SortStableFunc to sort a slice of integers while maintaining the relative order of equal elements.

Example

package main

import (
	"fmt"
	"slices"
)

func main() {
	// Define a slice of integers with repeated elements
	numbers := []int{5, 3, 4, 1, 2, 3, 5}

	// Define a custom comparison function for ascending order
	compareAscending := func(a, b int) int {
		return a - b
	}

	// Sort the slice in ascending order with stable sorting
	slices.SortStableFunc(numbers, compareAscending)

	// Print the sorted slice
	fmt.Println("Stably sorted slice:", numbers)
}

Output:

Stably sorted slice: [1 2 3 3 4 5 5]

Explanation:

  • The slices.SortStableFunc function sorts the numbers slice in ascending order while maintaining the relative order of the repeated elements (3 and 5).

Sorting a Slice of Structs with Stable Sorting

This example shows how to use slices.SortStableFunc to sort a slice of structs by a specific field while preserving the relative order of elements with equal values.

Example

package main

import (
	"fmt"
	"slices"
)

type Person struct {
	Name string
	Age  int
}

func main() {
	// Define a slice of Person structs with repeated ages
	people := []Person{
		{"Alice", 30},
		{"Bob", 25},
		{"Charlie", 30},
		{"Dave", 25},
	}

	// Define a custom comparison function to sort by Age
	compareByAge := func(a, b Person) int {
		return a.Age - b.Age
	}

	// Sort the slice by Age in ascending order with stable sorting
	slices.SortStableFunc(people, compareByAge)

	// Print the sorted slice
	for _, person := range people {
		fmt.Printf("%s: %d\n", person.Name, person.Age)
	}
}

Output:

Bob: 25
Dave: 25
Alice: 30
Charlie: 30

Explanation:

  • The slices.SortStableFunc function sorts the people slice by the Age field while preserving the relative order of people with the same age.

Custom Stable Sorting for Complex Types

This example demonstrates how slices.SortStableFunc can be used to sort a slice of complex types based on multiple fields while maintaining stability.

Example

package main

import (
	"fmt"
	"slices"
)

type Product struct {
	Name  string
	Price float64
	Rating float64
}

func main() {
	// Define a slice of Product structs with repeated ratings
	products := []Product{
		{"Laptop", 1000.0, 4.5},
		{"Phone", 800.0, 4.7},
		{"Tablet", 600.0, 4.5},
		{"Monitor", 200.0, 4.7},
	}

	// Define a custom comparison function to sort by Rating, then by Price
	compareByRatingAndPrice := func(a, b Product) int {
		if a.Rating != b.Rating {
			if a.Rating > b.Rating {
				return 1
			}
			return -1
		}
		if a.Price > b.Price {
			return 1
		}
		if a.Price < b.Price {
			return -1
		}
		return 0
	}

	// Sort the slice by Rating, then by Price in ascending order with stable sorting
	slices.SortStableFunc(products, compareByRatingAndPrice)

	// Print the sorted slice
	for _, product := range products {
		fmt.Printf("%s: Price: %.2f, Rating: %.1f\n", product.Name, product.Price, product.Rating)
	}
}

Output:

Tablet: Price: 600.00, Rating: 4.5
Laptop: Price: 1000.00, Rating: 4.5
Monitor: Price: 200.00, Rating: 4.7
Phone: Price: 800.00, Rating: 4.7

Explanation:

  • The slices.SortStableFunc function sorts the products slice by Rating and, within equal ratings, by Price while maintaining the original relative order of products with the same rating.

Real-World Use Case Example: Sorting Transactions by Amount and Date

A practical use case for slices.SortStableFunc is sorting a list of transactions by amount, while preserving the order of transactions that occurred on the same day.

Example: Sorting Transactions

package main

import (
	"fmt"
	"time"
	"slices"
)

type Transaction struct {
	Date   time.Time
	Amount float64
}

func main() {
	// Define a slice of Transaction structs
	transactions := []Transaction{
		{time.Date(2024, 7, 10, 0, 0, 0, 0, time.UTC), 100.0},
		{time.Date(2024, 7, 11, 0, 0, 0, 0, time.UTC), 200.0},
		{time.Date(2024, 7, 10, 0, 0, 0, 0, time.UTC), 50.0},
		{time.Date(2024, 7, 11, 0, 0, 0, 0, time.UTC), 150.0},
	}

	// Define a custom comparison function to sort by Amount
	compareByAmount := func(a, b Transaction) int {
		if a.Amount > b.Amount {
			return 1
		}
		if a.Amount < b.Amount {
			return -1
		}
		return 0
	}

	// Sort the slice by Amount in ascending order with stable sorting
	slices.SortStableFunc(transactions, compareByAmount)

	// Print the sorted transactions
	for _, transaction := range transactions {
		fmt.Printf("Date: %s, Amount: %.2f\n", transaction.Date.Format("2006-01-02"), transaction.Amount)
	}
}

Output:

Date: 2024-07-10, Amount: 50.00
Date: 2024-07-10, Amount: 100.00
Date: 2024-07-11, Amount: 150.00
Date: 2024-07-11, Amount: 200.00

Explanation:

  • The slices.SortStableFunc function sorts the transactions slice by Amount while maintaining the original order of transactions that occurred on the same date.

Conclusion

The slices.SortStableFunc function in Go is used for stable sorting of slices based on custom comparison logic. It ensures that the relative order of elements that compare as equal is preserved, making it ideal for scenarios where the original order of equivalent elements is important. By using slices.SortStableFunc, you can efficiently sort your data in Go applications while maintaining the stability of the sort.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top