A powerful, type-safe Go library providing generic utility functions for working with slices.
Working with slices is fundamental to Go programming, but the standard library doesn't provide many helper functions for common slice operations. This library fills that gap by providing type-safe, generic functions for filtering, mapping, flattening, grouping, and more.
Built with Go generics, this library offers:
- 🔒 Type safety - Catch errors at compile time, not runtime
- ⚡ Zero dependencies - Just pure Go (except for testing)
- 🧪 BDD tested - Comprehensive test coverage using Cucumber/Godog
- 📦 Easy to use - Simple, intuitive API
go get github.com/spandigital/slicesRequirements: Go 1.25 or later
package main
import (
"fmt"
"github.com/spandigital/slices"
)
func main() {
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8}
// Filter even numbers
evens := slices.Filter(numbers, func(n int) bool {
return n % 2 == 0
})
fmt.Println(evens) // [2, 4, 6, 8]
// Double all numbers
doubled := slices.Map(numbers, func(n int) int {
return n * 2
})
fmt.Println(doubled) // [2, 4, 6, 8, 10, 12, 14, 16]
// Get unique values
unique := slices.Unique([]int{1, 2, 2, 3, 3, 3})
fmt.Println(unique) // [1, 2, 3]
}The following functions are deprecated and will be removed in v1.0.0. Please migrate to the Go standard library equivalents:
| Deprecated Function | Standard Library Replacement | Available Since |
|---|---|---|
Contains |
slices.Contains |
Go 1.21 |
Index |
slices.Index |
Go 1.21 |
GroupByLen |
slices.Chunk |
Go 1.23 |
For Contains and Index, simply change the import:
// Old (deprecated)
import "github.com/spandigital/slices"
result := slices.Contains(mySlice, value)
// New (recommended)
import "slices"
result := slices.Contains(mySlice, value)For GroupByLen, use the iterator-based slices.Chunk:
// Old (deprecated)
chunks := slices.GroupByLen(mySlice, 3)
// New (recommended)
import "slices"
var chunks [][]int
for chunk := range slices.Chunk(mySlice, 3) {
chunks = append(chunks, chunk)
}Filter a slice based on a predicate function.
evens := slices.Filter([]int{1, 2, 3, 4, 5, 6}, func(v int) bool {
return v % 2 == 0
})
// Result: [2, 4, 6]Remove nil pointer values from a slice of pointers.
type Person struct{ Name string }
people := []*Person{
{Name: "Alice"},
nil,
{Name: "Bob"},
nil,
}
filtered := slices.FilterNil(people)
// Result: [{Name: "Alice"}, {Name: "Bob"}]Remove any nil-able values (pointers, interfaces, slices, maps, channels, functions) from a slice.
values := []any{1, nil, "hello", nil, 3.14}
clean := slices.RemoveNil(values)
// Result: [1, "hello", 3.14]Deprecated: Use slices.Contains from the standard library instead. This function will be removed in v1.0.0.
Check if a slice contains a specific value.
hasValue := slices.Contains([]int{1, 2, 3, 4, 5}, 3) // true
noValue := slices.Contains([]string{"foo", "bar"}, "baz") // falseDeprecated: Use slices.Index from the standard library instead. This function will be removed in v1.0.0.
Find the index of a value in a slice. Returns -1 if not found.
idx := slices.Index([]int{1, 2, 3, 4, 5}, 3) // 2
notFound := slices.Index([]string{"foo", "bar"}, "baz") // -1Get unique values from a slice (removes duplicates).
unique := slices.Unique([]int{1, 2, 2, 3, 3, 3, 4})
// Result: [1, 2, 3, 4] (order may vary)Find the intersection of multiple slices (values present in all slices).
common := slices.Intersection(
[]int{1, 2, 3, 4, 5},
[]int{3, 4, 5, 6, 7},
[]int{4, 5, 8, 9},
)
// Result: [4, 5]Transform each element in a slice using a mapping function.
doubled := slices.Map([]int{1, 2, 3, 4}, func(v int) int {
return v * 2
})
// Result: [2, 4, 6, 8]
lengths := slices.Map([]string{"a", "ab", "abc"}, func(s string) int {
return len(s)
})
// Result: [1, 2, 3]SyncMap[S ~[]V, V any, E any](ctx context.Context, s S, extract func(context.Context, V) (E, error)) ([]E, error)
Concurrently map over a slice using goroutines. All operations run in parallel.
import "context"
urls := []string{"https://api.example.com/1", "https://api.example.com/2"}
results, err := slices.SyncMap(context.Background(), urls, func(ctx context.Context, url string) (string, error) {
// Fetch data from URL concurrently
return fetchData(ctx, url)
})MapFrom[S ~[]I, I any, K comparable, V any](s S, extractKey func(I) K, extractValue func(I) V) map[K]V
Convert a slice to a map by extracting keys and values from each element.
type User struct {
ID int
Name string
}
users := []User{{ID: 1, Name: "Alice"}, {ID: 2, Name: "Bob"}}
userMap := slices.MapFrom(users,
func(u User) int { return u.ID },
func(u User) string { return u.Name },
)
// Result: map[int]string{1: "Alice", 2: "Bob"}Flatten a 2-dimensional slice into a 1-dimensional slice.
nested := [][]int{{1, 2}, {3, 4}, {5, 6}}
flat := slices.Flatten(nested)
// Result: [1, 2, 3, 4, 5, 6]Note: Also available: Flatten3, Flatten4 for 3D and 4D slices.
Group slice elements by a key extracted from each element.
type Person struct {
Name string
Age int
}
people := []Person{
{Name: "Alice", Age: 25},
{Name: "Bob", Age: 30},
{Name: "Charlie", Age: 25},
}
byAge := slices.GroupBy(people, func(p Person) int {
return p.Age
})
// Result: map[int][]Person{
// 25: [{Name: "Alice", Age: 25}, {Name: "Charlie", Age: 25}],
// 30: [{Name: "Bob", Age: 30}],
// }Deprecated: Use slices.Chunk from the standard library instead. This function will be removed in v1.0.0. Note that slices.Chunk returns an iterator, not a materialized slice.
Split a slice into chunks of a specified length.
numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9}
chunks := slices.GroupByLen(numbers, 3)
// Result: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
partialChunk := slices.GroupByLen([]int{1, 2, 3, 4, 5}, 2)
// Result: [[1, 2], [3, 4], [5]]Get a specific page from a slice (zero-indexed).
items := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
page1 := slices.Page(items, 3, 0) // [1, 2, 3]
page2 := slices.Page(items, 3, 1) // [4, 5, 6]
page4 := slices.Page(items, 3, 3) // [10]Calculate the number of pages needed for a given page size.
items := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
totalPages := slices.NumPages(items, 3) // 4Append only non-nil pointers to a slice.
type Item struct{ Value int }
items := []*Item{{Value: 1}}
items = slices.AppendNotNil(items, &Item{Value: 2}, nil, &Item{Value: 3})
// Result: [{Value: 1}, {Value: 2}, {Value: 3}]This library uses Behavior-Driven Development (BDD) with Cucumber/Godog. All functions are tested with comprehensive scenarios written in Gherkin.
# Run all tests
go test -v ./...
# Run specific function tests
go test -v -run TestFeatures/FilterWe welcome contributions! Please see our Contributing Guidelines for details.
This project uses:
- Conventional Commits for commit messages
- Pre-commit hooks for code quality (go-fmt, golangci-lint, tests)
- BDD testing with Godog
See LICENSE file for details.
This library is maintained by SPAN Digital.