Advanced Slice and Map Utilities
Provides functional-style utilities for slices and maps without sacrificing allocations or compile-time type checking.
main.go
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188
package main
import ( "fmt"
"sort")
// Filter returns a new slice containing only elements that satisfy the predicate
func Filter[T any](slice []T, predicate func(T) bool) []T {
result := make([]T, 0, len(slice))
for _, item := range slice {
if predicate(item) {
result = append(result, item)
}
}
return result
}
// Map applies a transformation function to each element and returns a new slice
func Map[T, U any](slice []T, transform func(T) U) []U {
result := make([]U, 0, len(slice))
for _, item := range slice {
result = append(result, transform(item))
}
return result
}
// Reduce reduces a slice to a single value using a reducer function
func Reduce[T, U any](slice []T, reducer func(U, T) U, initial U) U {
accumulator := initial
for _, item := range slice {
accumulator = reducer(accumulator, item)
}
return accumulator
}
// Find returns the first element that satisfies the predicate and a boolean indicating if found
func Find[T any](slice []T, predicate func(T) bool) (T, bool) {
for _, item := range slice {
if predicate(item) {
return item, true
}
}
var zero T
return zero, false
}
// RemoveDuplicates returns a new slice with duplicates removed (uses slices.Compare for equality)
func RemoveDuplicates[T comparable](slice []T) []T {
seen := make(map[T]bool)
result := make([]T, 0, len(slice))
for _, item := range slice {
if !seen[item] {
seen[item] = true
result = append(result, item)
}
}
return result
}
// GroupBy groups slice elements into a map based on a key function
func GroupBy[T any, K comparable](slice []T, keyFunc func(T) K) map[K][]T {
groups := make(map[K][]T)
for _, item := range slice {
key := keyFunc(item)
groups[key] = append(groups[key], item)
}
return groups
}
// Keys returns a slice of all keys in a map
func Keys[K comparable, V any](m map[K]V) []K {
keys := make([]K, 0, len(m))
for k := range m {
keys = append(keys, k)
}
return keys
}
// Values returns a slice of all values in a map
func Values[K comparable, V any](m map[K]V) []V {
values := make([]V, 0, len(m))
for _, v := range m {
values = append(values, v)
}
return values
}
// Chunk splits a slice into chunks of the specified size
func Chunk[T any](slice []T, chunkSize int) [][]T {
if chunkSize <= 0 {
panic("chunkSize must be greater than 0")
}
var chunks [][]T
for i := 0; i < len(slice); i += chunkSize {
end := i + chunkSize
if end > len(slice) {
end = len(slice)
}
chunks = append(chunks, slice[i:end])
}
return chunks
}
func main() {
// Example 1: Filter slice
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
evenNumbers := Filter(numbers, func(n int) bool {
return n%2 == 0
})
fmt.Println("Even numbers:", evenNumbers)
// Example 2: Map slice
strings := Map(numbers, func(n int) string {
return fmt.Sprintf("Number: %d", n)
})
fmt.Println("Mapped strings:", strings)
// Example 3: Reduce slice (sum)
sum := Reduce(numbers, func(acc, n int) int {
return acc + n
}, 0)
fmt.Println("Sum of numbers:", sum)
// Example 4: Find element
found, exists := Find(numbers, func(n int) bool {
return n == 7
})
fmt.Printf("Found 7: %v (exists: %t)\n", found, exists)
// Example 5: Remove duplicates
duplicates := []int{1, 2, 2, 3, 3, 3, 4, 5, 5}
unique := RemoveDuplicates(duplicates)
fmt.Println("Unique numbers:", unique)
// Example 6: Group by
type Person struct {
Name string
Age int
City string
}
people := []Person{
{Name: "John", Age: 30, City: "New York"},
{Name: "Jane", Age: 28, City: "London"},
{Name: "Bob", Age: 35, City: "New York"},
{Name: "Alice", Age: 25, City: "London"},
}
// Group people by city
peopleByCity := GroupBy(people, func(p Person) string {
return p.City
})
fmt.Println("People by city:")
for city, folks := range peopleByCity {
fmt.Printf(" %s: %v\n", city, folks)
}
// Example 7: Map keys and values
personAges := map[string]int{
"John": 30,
"Jane": 28,
"Bob": 35,
"Alice": 25,
}
fmt.Println("Map keys:", Keys(personAges))
fmt.Println("Map values:", Values(personAges))
// Example 8: Chunk slice
largeSlice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
chunks := Chunk(largeSlice, 3)
fmt.Println("Chunks (size 3):", chunks)
// Example 9: Combined operations
// Filter adults, map to names, sort
adultNames := Map(
Filter(people, func(p Person) bool { return p.Age >= 30 }),
func(p Person) string { return p.Name },
)
sort.Strings(adultNames)
fmt.Println("Adult names (sorted):", adultNames)
// Example 10: Reduce to concatenate strings
words := []string{"Hello", " ", "Go", " ", "Utilities!"}
sentence := Reduce(words, func(acc, s string) string {
return acc + s
}, "")
fmt.Println("Concatenated sentence:", sentence)
}How It Works
Provides functional-style utilities for slices and maps without sacrificing allocations or compile-time type checking.
Generic functions iterate slices with predicates or transformers, preallocate output where possible, use maps to de-duplicate or group values, and expose reduce and find helpers for concise data processing.
Key Concepts
- 1Uses type parameters so helpers stay strongly typed.
- 2Preallocates result slices and maps to reduce garbage.
- 3GroupBy and Dedup leverage maps for O(n) lookups.
- 4Includes Find and Reduce helpers for expressive pipelines.
When to Use This Pattern
- Transforming API responses before rendering.
- Cleaning and grouping analytics events in memory.
- Building small data processing utilities without heavier libraries.
- Sharing a common helper set across services to avoid duplication.
Best Practices
- Keep predicate and mapper functions pure to avoid side effects.
- Pre-size outputs when input length is known.
- Avoid modifying input slices while iterating.
- Benchmark critical paths because allocations vary by predicate.
Go Version1.18+
Difficultyintermediate
Production ReadyYes
Lines of Code188