Go is known for its simplicity and efficiency. However, one limitation that Go developers have faced for years is the lack of generics. This limitation often led to repetitive code when working with slices of structs, especially when you wanted to group them by a specific property. Thankfully, with the introduction of generics in Go 1.18, this task has become much cleaner and more elegant. In this blog post, we'll explore how to group a slice of structs by a specific property using generics.
The Problem
Imagine you have a slice of structs representing people, and you want to group them by their city of residence. Each person struct might look something like this:
1type Person struct {
2 Name string
3 Age int
4 City string
5}
Your goal is to create a function that takes this slice and groups the people by their city. In other words, you want a map where the keys are city names, and the values are slices of people living in those cities.
The Solution: A Generic Function
With Go 1.18 and the introduction of generics, you can create a generic function to group a slice of structs by a specific property. Here's the code for such a function:
1package main
2
3import (
4 "fmt"
5)
6
7// GroupByProperty groups a slice of structs by a specific property.
8func GroupByProperty[T any, K comparable](items []T, getProperty func(T) K) map[K][]T {
9 grouped := make(map[K][]T)
10
11 for _, item := range items {
12 key := getProperty(item)
13 grouped[key] = append(grouped[key], item)
14 }
15
16 return grouped
17}
Let's break down this code:
-
GroupByProperty
is a generic function that takes two parameters:-
items []T
: A slice of any typeT
. -
getProperty func(T) K
: A function that extracts a property of typeK
from each element of the slice.
-
Inside the function:
-
We initialize an empty map called
grouped
where the keys will be the property values (K
), and the values will be slices of elements ([]T
) that share the same property value. -
We loop through the
items
slice and use thegetProperty
function to extract the property value (key
) for each element. We then append the current element to the corresponding slice in thegrouped
map. -
Finally, we return the
grouped
map containing the grouped elements.
Putting It into Action
Now that we have our generic function, let's use it to group our Person
structs by the City
property:
1func main() {
2 people := []Person{
3 {Name: "Alice", Age: 25, City: "New York"},
4 {Name: "Bob", Age: 30, City: "Los Angeles"},
5 {Name: "Charlie", Age: 25, City: "New York"},
6 {Name: "David", Age: 35, City: "Chicago"},
7 }
8
9 // Group people by the "City" property.
10 groupedByCity := GroupByProperty(people, func(p Person) string {
11 return p.City
12 })
13
14 // Print the grouped data.
15 for city, group := range groupedByCity {
16 fmt.Printf("City: %s\n", city)
17 for _, person := range group {
18 fmt.Printf(" Name: %s, Age: %d\n", person.Name, person.Age)
19 }
20 }
21}
In this code:
-
We call
GroupByProperty
with ourpeople
slice and a function that extracts theCity
property from eachPerson
. -
The result,
groupedByCity
, is a map where the keys are city names, and the values are slices ofPerson
structs who have that city as their property value. -
Finally, we print the grouped data.
Conclusion
Thanks to the introduction of generics in Go 1.18, tasks like grouping a slice of structs by a specific property have
become much more elegant and maintainable. The GroupByProperty
function we created is a generic solution that can be
used to group slices of any type of struct by any property, making your code cleaner and more reusable. As Go continues
to evolve, it's exciting to see how generics will simplify and improve various aspects of the language. Happy coding!
If this post was enjoyable or useful for you, please share it! If you have comments, questions, or feedback, you can email my personal email. To get new posts, subscribe use the RSS feed.