framework
framework copied to clipboard
feat: laravel collection
📑 Description
Closes https://github.com/goravel/goravel/issues/566
Basic Usage
Creating Collections
// Create from variadic arguments
numbers := collect.New(1, 2, 3, 4, 5)
// Create from slice
items := []string{"apple", "banana", "cherry"}
fruits := collect.Of(items)
Basic Operations
numbers := collect.New(1, 2, 3, 4, 5)
// Get count
fmt.Println(numbers.Count()) // 5
// Get first/last
fmt.Println(*numbers.First()) // 1
fmt.Println(*numbers.Last()) // 5
// Check if empty
fmt.Println(numbers.IsEmpty()) // false
// Get all items
fmt.Println(numbers.All()) // [1 2 3 4 5]
Filtering and Mapping
numbers := collect.New(1, 2, 3, 4, 5, 6)
// Filter even numbers
evens := numbers.Filter(func(n int, _ int) bool {
return n%2 == 0
})
fmt.Println(evens.All()) // [2 4 6]
// Map to double values (Laravel-style method)
doubled := numbers.Map(func(n int, _ int) interface{} {
return n * 2
})
fmt.Println(doubled.All()) // [2 4 6 8 10 12]
// Map to string representation
strings := numbers.Map(func(n int, i int) interface{} {
return fmt.Sprintf("item_%d_%d", i, n)
})
fmt.Println(strings.All()) // [item_0_1 item_1_2 item_2_3 item_3_4 item_4_5 item_5_6]
### Working with Structs
```go
type User struct {
ID int
Name string
Age int
}
users := collect.Of([]User{
{ID: 1, Name: "Alice", Age: 25},
{ID: 2, Name: "Bob", Age: 30},
{ID: 3, Name: "Charlie", Age: 25},
})
// Filter by struct field using different Where patterns
youngUsers := users.Where("Age", "=", 25) // 3 parameters
youngUsers = users.Where("Age", 25) // 2 parameters (implies '=')
fmt.Println(youngUsers.Count()) // 2
// Using callback function
adultUsers := users.Where(func(u User) bool {
return u.Age >= 18
})
// Using different operators
olderUsers := users.Where("Age", ">", 25)
notCharlie := users.Where("Name", "!=", "Charlie")
// Handling null values
activeUsers := users.Where("DeletedAt", "=", nil) // Find non-deleted users
deletedUsers := users.Where("DeletedAt", "!=", nil) // Find deleted users
// Group by field
grouped := users.GroupBy(func(u User) string {
return fmt.Sprintf("%d", u.Age)
})
fmt.Println(len(grouped)) // 2 groups
// Pluck field values
names := users.Pluck("Name")
fmt.Println(names.Count()) // 3
// Sort by field
sorted := users.SortBy(func(u User) string {
return u.Name
})
fmt.Println(sorted.First().Name) // "Alice"
Aggregation Methods
numbers := collect.New(1, 2, 3, 4, 5)
// Sum
sum := numbers.Sum(func(n int) float64 {
return float64(n)
})
fmt.Println(sum) // 15.0
// Average
avg := numbers.Avg(func(n int) float64 {
return float64(n)
})
fmt.Println(avg) // 3.0
// Min/Max
min := numbers.Min(func(n int) float64 { return float64(n) })
max := numbers.Max(func(n int) float64 { return float64(n) })
fmt.Println(min, max) // 1.0 5.0
Conditional Operations
numbers := collect.New(1, 2, 3, 4, 5)
// Conditional transformations
result := numbers.
When(true, func(c *collect.Collection[int]) *collect.Collection[int] {
return c.Filter(func(n int, _ int) bool { return n > 2 })
}).
Unless(false, func(c *collect.Collection[int]) *collect.Collection[int] {
return c.Take(2)
})
fmt.Println(result.All()) // [3 4]
// Tap for side effects
numbers.Tap(func(c *collect.Collection[int]) {
fmt.Println("Processing", c.Count(), "items")
})
Available Methods
Core Methods (Alphabetical)
-
After(value)- Get item after given value -
All()- Get all items as slice -
Average(keyFunc)- Calculate average using key function -
Before(value)- Get item before given value -
Chunk(size)- Split into chunks of given size -
Clone()- Create a copy of the collection -
Collapse()- Collapse nested arrays into single array -
Combine(keys)- Combine with keys to create map -
Contains(value)- Check if collection contains value -
Count()- Get item count -
Diff(other)- Get difference with another collection -
Each(func)- Iterate over each item -
Every(predicate)- Check if all items match predicate -
Filter(predicate)- Filter items by predicate -
First()- Get first item -
Flatten()- Flatten nested structures -
GroupBy(keyFunc)- Group items by key function -
Intersect(other)- Get intersection with another collection -
IsEmpty()- Check if collection is empty -
IsNotEmpty()- Check if collection is not empty -
Join(separator)- Join items with separator -
Last()- Get last item -
Map(func)- Transform each item with a function (returns Collection[interface{}]) -
Merge(other)- Merge with another collection -
Partition(predicate)- Split into two collections by predicate -
Pluck(field)- Extract field values -
Push(items...)- Add items to end -
Reverse()- Reverse order -
Search(value)- Find index of value -
Slice(start, length)- Get slice of items -
Sort(lessFunc)- Sort by comparison function -
SortBy(keyFunc)- Sort by key function -
Sum(keyFunc)- Calculate sum using key function -
Take(n)- Take first n items -
Unique()- Get unique items -
Where(field, operator, value)- Filter by field comparison -
Zip(other)- Zip with another collection
Where Method - Laravel-style Filtering
The Where method supports multiple patterns for flexible filtering:
type User struct {
ID int
Name string
Age int
Country string
Balance float64
DeletedAt *time.Time
}
users := collect.Of([]User{...})
// 1. Two parameters (field, value) - implies '=' operator
frenchUsers := users.Where("Country", "FR")
youngUsers := users.Where("Age", 25)
// 2. Three parameters (field, operator, value)
richUsers := users.Where("Balance", ">", 100.0)
nonFrenchUsers := users.Where("Country", "!=", "FR")
seniorUsers := users.Where("Age", ">=", 65)
// 3. Single parameter (callback function)
customFilter := users.Where(func(u User) bool {
return u.Age > 18 && u.Country == "US"
})
// 4. Null comparisons
activeUsers := users.Where("DeletedAt", "=", nil)
deletedUsers := users.Where("DeletedAt", "!=", nil)
// 5. String operations
nameContains := users.Where("Name", "like", "john")
excludePattern := users.Where("Name", "not like", "test")
Supported Operators:
-
=,==- Equality -
!=- Inequality -
>,>=- Greater than, Greater than or equal -
<,<=- Less than, Less than or equal -
like- Case-insensitive substring match -
not like- Case-insensitive substring exclusion
Utility Methods
-
Debug()- Print collection contents -
Dump()- Print collection contents -
Tap(func)- Execute function and return collection -
ToJSON()- Convert to JSON string -
When(condition, func)- Execute function if condition is true -
Unless(condition, func)- Execute function if condition is false
Map Method - Laravel-style Transformation
The Map method provides Laravel-style transformation capabilities:
type User struct {
ID int
Name string
Age int
}
users := collect.Of([]User{
{ID: 1, Name: "Alice", Age: 25},
{ID: 2, Name: "Bob", Age: 30},
})
// Transform to different types
names := users.Map(func(u User, i int) interface{} {
return u.Name
})
ages := users.Map(func(u User, i int) interface{} {
return u.Age
})
// Complex transformations
summaries := users.Map(func(u User, i int) interface{} {
return map[string]interface{}{
"id": u.ID,
"summary": fmt.Sprintf("%s (%d years)", u.Name, u.Age),
"index": i,
}
})
// Chain with other operations
result := users.
Map(func(u User, i int) interface{} {
return u.Name
}).
Filter(func(name interface{}, _ int) bool {
return len(name.(string)) > 3
})
Generic Functions
Some operations require type transformation and are provided as generic functions for type safety:
// Type-safe Map to different type
strings := collect.New("1", "2", "3")
numbers := collect.Map(strings, func(s string, _ int) int {
n, _ := strconv.Atoi(s)
return n
})
// Reduce to single value
sum := collect.Reduce(numbers, func(acc int, n int, _ int) int {
return acc + n
}, 0)
// Pluck with type conversion
users := collect.Of([]User{...})
userIDs := collect.Pluck[User, int](users, "ID")
Testing
go test -v
Examples
See example_test.go for comprehensive usage examples.
LazyCollection
LazyCollection provides lazy evaluation for efficient processing of large datasets. Operations are not executed until a terminal operation is called.
Creating LazyCollections
// From slice
lazy := collect.LazyCollect([]int{1, 2, 3, 4, 5})
// From range
lazy := collect.LazyRange(1, 1000000)
// From generator function
lazy := collect.LazyGenerate(func(i int) int {
return i * 2
}, 100)
// From channel
ch := make(chan int, 5)
// ... populate channel
lazy := collect.LazyFromChannel(ch)
Lazy Operations
// Chain operations - these don't execute until consumed
result := collect.LazyRange(1, 1000000).
Filter(func(n int, _ int) bool { return n%2 == 0 }).
Take(5).
All() // This triggers execution
fmt.Println(result) // [2 4 6 8 10]
Performance Benefits
// Efficient for large datasets when only a portion is needed
result := collect.LazyRange(1, 1000000).
Filter(func(n int, _ int) bool { return n%100 == 0 }).
Take(10).
All()
// Only processes what's needed, not all 1 million items
Lazy vs Eager
// Lazy - processes only what's needed
lazyResult := collect.LazyRange(1, 1000000).
Filter(func(n int, _ int) bool { return n%2 == 0 }).
Take(5).
All()
// Eager - processes all items first
eagerResult := collect.New(/* large slice */).
Filter(func(n int, _ int) bool { return n%2 == 0 }).
Take(5).
All()
Converting Between Collections
// Lazy to eager
lazy := collectioncollectLazyRange(1, 11)
eager := lazy.Collect()
// Eager to lazy
eager := collect.New(1, 2, 3, 4, 5)
lazy := collect.LazyCollect(eager.All())
LazyCollection Methods
-
All()- Materialize all items -
Count()- Count items -
Filter(predicate)- Filter items -
Map(func)- Transform each item with a function (returns LazyCollection[interface{}]) -
Where(params...)- Laravel-style filtering (supports all same patterns as Collection) -
Take(n)- Take first n items -
Skip(n)- Skip first n items -
TakeWhile(predicate)- Take while condition is true -
DropWhile(predicate)- Drop while condition is true -
Unique()- Get unique items -
Sort(lessFunc)- Sort items -
Reverse()- Reverse order -
Sum(keyFunc)- Calculate sum -
Average(keyFunc)- Calculate average -
Min(keyFunc)- Find minimum -
Max(keyFunc)- Find maximum -
GroupBy(keyFunc)- Group items -
Partition(predicate)- Split into two collections -
FlatMap(func)- Flat map transformation -
Each(func)- Execute function for each item -
ForEach(func)- Execute function for each item (consumes collection) -
Iterator()- Get iterator for manual control -
Collect()- Convert to eager Collection
License
MIT License
✅ Checks
- [ ] Added test cases for my code