Filtering slices is a common operation in Go, especially when dealing with collections of data structures or composite types. In Go, a composite type is one that is made up of several types, such as structs, arrays, or slices themselves. This article will guide you through the process of filtering composite types specifically within a slice, starting from basic to more advanced examples.
Basic Example: Filtering a Slice of Integers
Before we dive into composite types, it’s helpful to understand how filtering works in simpler scenarios, like filtering a slice of integers. Filters remove elements that do not meet specific criteria, producing a new slice.
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
evenNumbers := filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println(evenNumbers) // Output: [2, 4, 6]
}
func filter(nums []int, test func(int) bool) []int {
var result []int
for _, v := range nums {
if test(v) {
result = append(result, v)
}
}
return result
}
Intermediate Example: Filtering a Slice of Structs
In real-world applications, data is often stored in custom structs. Here’s how to filter a slice of structs based on one or more struct fields.
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
filteredPeople := filterPeople(people, func(p Person) bool {
return p.Age > 30
})
fmt.Println(filteredPeople) // Output: [{Charlie 35}]
}
func filterPeople(people []Person, test func(Person) bool) []Person {
var result []Person
for _, p := range people {
if test(p) {
result = append(result, p)
}
}
return result
}
Advanced Example: Using Generics for Slice Filtering
With the introduction of generics in Go 1.18, we can write more abstract and reusable filter functions that work for different types.
package main
import "fmt"
func main() {
numbers := []int{1, 2, 3, 4, 5, 6}
evenNumbers := genericFilter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println(evenNumbers) // Output: [2, 4, 6]
people := []Person{
{"Alice", 30},
{"Bob", 25},
{"Charlie", 35},
}
filteredPeople := genericFilter(people, func(p Person) bool {
return p.Age > 30
})
fmt.Println(filteredPeople) // Output: [{Charlie 35}]
}
type Person struct {
Name string
Age int
}
func genericFilter[T any](slice []T, test func(T) bool) []T {
var result []T
for _, v := range slice {
if test(v) {
result = append(result, v)
}
}
return result
}
Generics allow for a cleaner and more versatile filtering approach, letting you reuse `genericFilter` for any type without rewriting the function for each new use case.