Collections
The collect package brings Laravel-style generic collections to Go. Wrap any slice with collect.Collect(items) to unlock a rich, chainable API for filtering, sorting, aggregating, and transforming data without writing manual loops.
Concept
Collection[T] is a generic wrapper built on Go 1.18+ generics. Transformation methods return a new Collection[T] for chaining. Terminal methods like First(), Sum(), and ToSlice() extract values. Because Go methods cannot introduce new type parameters, cross-type operations like Map and GroupBy are provided as package-level functions.
import "github.com/CodeSyncr/nimbus/collect"
// Filter active users, sort by age, get the top 5
top5 := collect.Collect(users).
Filter(func(u User) bool { return u.IsActive }).
Sort(func(a, b User) bool { return a.Age < b.Age }).
Take(5).
ToSlice()
Available Methods
Transformation (Chainable)
| Method | Description |
|---|---|
Filter(fn) | Keep items where fn returns true |
Reject(fn) | Remove items where fn returns true |
Unique() | Deduplicate via deep equality |
Compact() | Remove zero-value items |
Reverse() | Reverse the order |
Shuffle() | Randomize order |
Sort(fn) / SortDesc(fn) | Stable sort asc/desc |
Take(n) / TakeLast(n) | First/last n items |
Skip(n) / SkipLast(n) | Skip first/last n items |
Slice(start, end) | Sub-slice by index |
Chunk(size) | Split into batches of given size |
Flatten() | Flatten one level deep |
Append(...items) | Add items to end |
Prepend(...items) | Add items to beginning |
Tap(fn) | Debug tap — call fn, return self |
Cross-Type Mappers (Package-Level Functions)
// Map to a different type
names := collect.MapCollect(users, func(u User) string { return u.Name })
// Group by a key
byRole := collect.GroupByCollect(users, func(u User) string { return u.Role })
// Key by unique field
byID := collect.KeyByCollect(users, func(u User) uint { return u.ID })
// Zip two collections
pairs := collect.ZipCollect(names, ages) // Collection[Pair[string, int]]
Terminal (Aggregation)
| Method | Returns | Description |
|---|---|---|
Count() | int | Number of items |
First() / Last() | (T, bool) | First/last item + found |
Nth(n) | (T, bool) | Item at index n |
Random() | (T, bool) | Random item |
Contains(fn) / Some(fn) | bool | Any item matches predicate |
Every(fn) | bool | All items match predicate |
Sum(fn) / Min(fn) / Max(fn) / Avg(fn) | float64 | Numeric aggregates via accessor |
ToSlice() | []T | Extract the underlying slice |
Each(fn) | — | Iterate with index |
Real-Life Example: Dashboard Analytics
func (ctrl *DashboardController) Stats(c *http.Context) error {
var orders []Order
database.Get().Where("created_at > ?", timex.Now().SubDays(30).Time()).Find(&orders)
coll := collect.Collect(orders)
stats := map[string]any{
"totalRevenue": coll.Sum(func(o Order) float64 { return o.Total }),
"avgOrder": coll.Avg(func(o Order) float64 { return o.Total }),
"maxOrder": coll.Max(func(o Order) float64 { return o.Total }),
"count": coll.Count(),
}
return c.JSON(200, stats)
}
Real-Life Example: Batch Processing with Chunk
func sendBulkEmails(users []User) {
batches := collect.Collect(users).Chunk(50)
for _, batch := range batches {
batch.Each(func(i int, u User) {
mail.Send(u.Email, "newsletter", map[string]any{"name": u.Name})
})
time.Sleep(time.Second) // rate limit between batches
}
}
Real-Life Example: Grouping API Responses
func (ctrl *ProductController) ByCategory(c *http.Context) error {
var products []Product
database.Get().Find(&products)
grouped := collect.GroupByCollect(
collect.Collect(products),
func(p Product) string { return p.Category },
)
return c.JSON(200, grouped)
// { "electronics": [...], "clothing": [...] }
}
Best Practices
- Use
Filter+Sort+Takechains instead of raw SQL for simple in-memory operations - Use
Chunk()for batch processing to avoid memory spikes and API rate limits - Use
Tap()during development to debug intermediate chain values - Use
GroupByCollectto structure flat query results into nested API responses - Prefer
Every()andSome()over manual loops for validation checks