🚀 slices
A powerful, type-safe Go library providing generic utility functions for working with slices.

Why use this library?
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
Installation
go get github.com/spandigital/slices
Requirements: Go 1.25 or later
Quick Start
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]
}
⚠️ Deprecation Notice
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 |
Migration Examples
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)
}
Available Functions
Filtering & Selection
Filter[S ~[]V, V any](s S, predicate func(V) bool) []V
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]
FilterNil[T any](in []*T) []*T
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"}]
RemoveNil[T any](in []T) []T
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]
Contains[S ~[]E, E comparable](s S, v E) bool ⚠️ Deprecated
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") // false
Index[S ~[]E, E comparable](s S, v E) int ⚠️ Deprecated
Deprecated: 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") // -1
Unique[T comparable](s []T) []T
Get 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)
Intersection[T cmp.Ordered](slices ...[]T) []T
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]
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)
})
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[T any](s [][]T) []T
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.
Grouping & Organization
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]]
Page[V any](s []V, pageSize int, pageIndex int) []V
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]
NumPages[V any](s []V, pageSize int) int
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) // 4
Appending
AppendNotNil[T any](in []*T, values ...*T) []*T
Append 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}]
Testing
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/Filter
Contributing
We 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
License
See LICENSE file for details.
Maintainers
This library is maintained by SPAN Digital.