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)

MethodDescription
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)

MethodReturnsDescription
Count()intNumber 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)boolAny item matches predicate
Every(fn)boolAll items match predicate
Sum(fn) / Min(fn) / Max(fn) / Avg(fn)float64Numeric aggregates via accessor
ToSlice()[]TExtract 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 + Take chains 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 GroupByCollect to structure flat query results into nested API responses
  • Prefer Every() and Some() over manual loops for validation checks