Learn how to use custom functions to compact slices in Go. Includes practical examples.
last modified April 20, 2025
This tutorial explains how to use the slices.CompactFunc function in Go. We’ll cover slice operations with practical examples of compacting slices.
The slices.CompactFunc function replaces consecutive equal elements with a single copy using a custom comparison function. It’s part of Go’s slices package.
This function is useful for removing duplicates from sorted slices when equality needs custom logic. It modifies the slice in place and returns the new length.
The simplest use of slices.CompactFunc removes consecutive duplicates from a slice of integers. We define a comparison function for equality.
basic_compact.go
package main
import ( “fmt” “slices” )
func main() { numbers := []int{1, 1, 2, 3, 3, 3, 4, 5, 5}
unique := slices.CompactFunc(numbers, func(a, b int) bool {
return a == b
})
fmt.Println("Compacted slice:", numbers[:unique])
}
We create a slice with consecutive duplicates and remove them. The comparison function checks for equality between adjacent elements.
slices.CompactFunc can compact strings case-insensitively. This example treats different cases as equal when they’re consecutive.
case_insensitive.go
package main
import ( “fmt” “slices” “strings” )
func main() { words := []string{“apple”, “Apple”, “banana”, “BANANA”, “cherry”}
unique := slices.CompactFunc(words, func(a, b string) bool {
return strings.EqualFold(a, b)
})
fmt.Println("Case-insensitive compact:", words[:unique])
}
The comparison uses strings.EqualFold for case-insensitive equality. Consecutive words with different cases are considered duplicates.
We can use slices.CompactFunc with custom struct types. This example compacts a slice of points based on their coordinates.
struct_compact.go
package main
import ( “fmt” “slices” )
type Point struct { X, Y int }
func main() { points := []Point{ {1, 2}, {1, 2}, {3, 4}, {3, 4}, {3, 4}, {5, 6}, }
unique := slices.CompactFunc(points, func(a, b Point) bool {
return a.X == b.X && a.Y == b.Y
})
fmt.Println("Unique points:", points[:unique])
}
The function checks both X and Y coordinates for equality. Consecutive duplicate points are removed from the slice.
Complex comparison logic can be implemented in the function. This example compacts numbers considering them equal if their difference is less than 0.5.
custom_logic.go
package main
import ( “fmt” “math” “slices” )
func main() { numbers := []float64{1.0, 1.2, 1.6, 2.0, 2.1, 2.9, 3.0}
unique := slices.CompactFunc(numbers, func(a, b float64) bool {
return math.Abs(a-b) < 0.5
})
fmt.Println("Approximately unique:", numbers[:unique])
}
The comparison uses floating-point math to determine approximate equality. Numbers close to each other are considered duplicates.
slices.CompactFunc handles empty slices gracefully. This example demonstrates its behavior with empty and nil slices.
empty_slice.go
package main
import ( “fmt” “slices” )
func main() { var empty []int nilSlice := []string(nil)
emptyResult := slices.CompactFunc(empty, func(a, b int) bool {
return a == b
})
nilResult := slices.CompactFunc(nilSlice, func(a, b string) bool {
return a == b
})
fmt.Println("Empty slice result:", emptyResult)
fmt.Println("Nil slice result:", nilResult)
}
Both empty and nil slices return 0 as the new length. The original slices remain unchanged as there are no elements to compact.
For large slices, the performance of the comparison function matters. This example benchmarks different comparison approaches.
performance.go
package main
import ( “fmt” “slices” “time” )
func main() { largeSlice := make([]int, 1_000_000) for i := range largeSlice { largeSlice[i] = i % 10 // Create many duplicates }
// Simple comparison
start := time.Now()
_ = slices.CompactFunc(largeSlice, func(a, b int) bool {
return a == b
})
fmt.Println("Simple comparison:", time.Since(start))
// Complex comparison
start = time.Now()
_ = slices.CompactFunc(largeSlice, func(a, b int) bool {
return a%2 == b%2 // Group by even/odd
})
fmt.Println("Complex comparison:", time.Since(start))
}
The execution time depends on the comparison complexity. slices.CompactFunc processes elements sequentially, modifying the slice in place.
This practical example uses slices.CompactFunc to remove consecutive duplicate log entries while preserving the original order.
log_deduplication.go
package main
import ( “fmt” “slices” “strings” )
type LogEntry struct { Timestamp string Message string }
func main() { logs := []LogEntry{ {“2023-01-01T10:00:00”, “System started”}, {“2023-01-01T10:00:05”, “User logged in”}, {“2023-01-01T10:00:05”, “User logged in”}, // Duplicate {“2023-01-01T10:01:00”, “File saved”}, {“2023-01-01T10:01:00”, “File saved”}, // Duplicate {“2023-01-01T10:02:00”, “System shutdown”}, }
unique := slices.CompactFunc(logs, func(a, b LogEntry) bool {
return a.Timestamp == b.Timestamp &&
strings.EqualFold(a.Message, b.Message)
})
fmt.Println("Deduplicated logs:")
for _, log := range logs[:unique] {
fmt.Printf("%s: %s\n", log.Timestamp, log.Message)
}
}
We compare both timestamp and message (case-insensitive) to identify duplicates. The result preserves the first occurrence of each unique log entry.
Go experimental slices package documentation
This tutorial covered the slices.CompactFunc function in Go with practical examples of compacting slices with custom comparison logic in various scenarios.
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.
List all Go tutorials.