Conversation
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #1134 +/- ##
==========================================
+ Coverage 66.81% 69.22% +2.40%
==========================================
Files 214 266 +52
Lines 14050 16833 +2783
==========================================
+ Hits 9387 11652 +2265
- Misses 4287 4685 +398
- Partials 376 496 +120 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
support/collect/collection.go
Outdated
| return &Collection[T]{items: flattened} | ||
| } | ||
|
|
||
| func Collect[T any](items []T) *Collection[T] { |
There was a problem hiding this comment.
How about changing collect.Collect to collect.Of? This way, it avoids redundancy and becomes more concise.
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive Laravel-style collection library for Go, providing both eager (Collection) and lazy (LazyCollection) evaluation strategies with over 100 methods for data manipulation.
Key Changes:
- Introduces generic Collection and LazyCollection types with extensive method sets
- Implements Laravel-style Where filtering with multiple parameter patterns
- Provides lazy evaluation support for efficient processing of large datasets
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
support/collect/collection.go |
Core eager collection implementation with 100+ methods for filtering, mapping, sorting, and aggregation operations |
support/collect/collection_test.go |
Comprehensive test suite covering all collection methods with edge cases and error scenarios |
support/collect/lazy_collection.go |
Lazy evaluation implementation using channels and goroutines for efficient streaming operations |
support/collect/lazy_collection_test.go |
Test suite for lazy collection functionality including performance tests and lazy evaluation validation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| } | ||
| result = append(result, []T{item1, item2}) | ||
| } | ||
|
|
There was a problem hiding this comment.
Potential goroutine leak: The Zip method may leave the ch2 channel's goroutine running if ch1 completes first. After breaking from the loop at line 855, any remaining items in ch2 won't be consumed, potentially blocking the goroutine that's feeding it.
Consider draining ch2 after the loop:
// Drain remaining items from ch2 to prevent goroutine leak
for range ch2 {
}| // Drain remaining items from ch2 to prevent goroutine leak | |
| for range ch2 { | |
| } |
| func (lc *LazyCollection[T]) Take(n int) *LazyCollection[T] { | ||
| newPipeline := make([]func(<-chan T) <-chan T, len(lc.pipeline)) | ||
| copy(newPipeline, lc.pipeline) | ||
|
|
||
| newPipeline = append(newPipeline, func(input <-chan T) <-chan T { | ||
| output := make(chan T) | ||
| go func() { | ||
| defer close(output) | ||
| taken := 0 | ||
| for item := range input { | ||
| if taken >= n { | ||
| break | ||
| } | ||
| output <- item | ||
| taken++ | ||
| } | ||
| }() | ||
| return output | ||
| }) |
There was a problem hiding this comment.
Potential goroutine leak: When Take breaks early at line 669, the input channel may not be fully consumed, which could block upstream goroutines. This is a common issue in lazy evaluation pipelines.
Consider draining the input channel after breaking:
for item := range input {
if taken >= n {
// Drain remaining items to prevent blocking upstream
for range input {
}
break
}
output <- item
taken++
}| func (lc *LazyCollection[T]) TakeWhile(predicate func(T) bool) *LazyCollection[T] { | ||
| newPipeline := make([]func(<-chan T) <-chan T, len(lc.pipeline)) | ||
| copy(newPipeline, lc.pipeline) | ||
|
|
||
| newPipeline = append(newPipeline, func(input <-chan T) <-chan T { | ||
| output := make(chan T) | ||
| go func() { | ||
| defer close(output) | ||
| for item := range input { | ||
| if !predicate(item) { | ||
| break | ||
| } | ||
| output <- item | ||
| } | ||
| }() | ||
| return output | ||
| }) |
There was a problem hiding this comment.
Potential goroutine leak: Similar to Take, when TakeWhile breaks early at line 694, the input channel may not be fully consumed, potentially blocking upstream goroutines.
Consider draining the input channel after breaking to prevent goroutine leaks.
| func (lc *LazyCollection[T]) Some(predicate func(T) bool) bool { | ||
| ch := lc.execute() | ||
| for item := range ch { | ||
| if predicate(item) { | ||
| return true | ||
| } | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
Potential goroutine leak: When Some returns early at line 617, the remaining items in the channel won't be consumed, potentially blocking upstream goroutines.
Consider draining the channel after returning true:
if predicate(item) {
// Drain to prevent blocking
go func() { for range ch {} }()
return true
}| func (lc *LazyCollection[T]) First() *T { | ||
| ch := lc.execute() | ||
| for item := range ch { | ||
| return &item | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
Potential goroutine leak: When First returns early at line 248, remaining items in the channel won't be consumed, potentially blocking upstream goroutines. Similar issue exists in FirstWhere (lines 261-268) and Every (lines 210-218).
Consider draining the channel after early returns.
| func (c *Collection[T]) Zip(other *Collection[T]) [][]T { | ||
| maxLen := len(c.items) | ||
| if len(other.items) > maxLen { | ||
| maxLen = len(other.items) | ||
| } | ||
|
|
||
| var result [][]T | ||
| for i := 0; i < maxLen; i++ { | ||
| var pair []T | ||
| if i < len(c.items) { | ||
| pair = append(pair, c.items[i]) | ||
| } | ||
| if i < len(other.items) { | ||
| pair = append(pair, other.items[i]) | ||
| } | ||
| result = append(result, pair) | ||
| } | ||
|
|
||
| return result | ||
| } |
There was a problem hiding this comment.
The Zip method in Collection includes partial pairs when collections have different lengths, while LazyCollection.Zip stops at the shorter collection. This inconsistency in behavior could confuse users.
Collection.Zip produces: [[1], [2, 3]] for [1, 2] and [3]
LazyCollection.Zip produces: [] for the same inputs
Consider making the behavior consistent between both implementations. The Laravel-style typically stops at the shorter collection (like LazyCollection does).
| return &Collection[interface{}]{items: mapped} | ||
| } | ||
|
|
||
| // MapCollect it will be renamed to Map in next release |
There was a problem hiding this comment.
Grammar issue: "it will be renamed" should be "It will be renamed" (capitalize first letter of sentence).
| // MapCollect it will be renamed to Map in next release | |
| // MapCollect It will be renamed to Map in next release |
hwbrzzl
left a comment
There was a problem hiding this comment.
Amazing PR 👍 @ahmed3mar Could you confirm the Copilot comments? Basically, LGTM.
| func (it *lazyIterator[T]) Reset() { | ||
| it.done = false | ||
| } |
There was a problem hiding this comment.
I think the function can be used as expect when it.ch is closed, could you confirm this and add a test case for it?
| taken := 0 | ||
| for item := range input { | ||
| if taken >= n { | ||
| break |
There was a problem hiding this comment.
There may still be data in the input chan, but break directly. goroutine will leak. The same as TakeWhile
| defer close(output) | ||
| seen := make(map[string]bool) | ||
| for item := range input { | ||
| key := fmt.Sprintf("%v", item) |
There was a problem hiding this comment.
The key will duplicated when item is struct or map, is there a better way to deal with such situation?
| ch1 := lc.execute() | ||
| ch2 := other.execute() | ||
|
|
||
| for item1 := range ch1 { | ||
| item2, ok2 := <-ch2 |
There was a problem hiding this comment.
The same as above, ch1 and ch2 may be leak.
| ch1 := lc.execute() | |
| ch2 := other.execute() | |
| for item1 := range ch1 { | |
| item2, ok2 := <-ch2 | |
| ch1 := lc.execute() | |
| ch2 := other.execute() | |
| defer func() { | |
| go func() { for range ch1 {} }() | |
| go func() { for range ch2 {} }() | |
| }() | |
| for item1 := range ch1 { | |
| item2, ok2 := <-ch2 |
| seen[key] = true | ||
| } | ||
| } | ||
| return &Collection[T]{items: duplicates} |
There was a problem hiding this comment.
If the collecction is 3,3,3, duplicates will be 3,3. It's unexpected.
| var duplicates []T | ||
|
|
||
| for _, item := range c.items { | ||
| key := fmt.Sprintf("%v", item) |
There was a problem hiding this comment.
The same as LazyCollection, %v is unused for struct and map.
krishankumar01
left a comment
There was a problem hiding this comment.
The implementation and the wide range of methods are solid. Instead of implementing everything in a single PR, should we break it into smaller PRs so that reviewing becomes easier?
cc: @hwbrzzl
| return 0 | ||
| } | ||
|
|
||
| max := keyFunc(c.items[0]) |
There was a problem hiding this comment.
How about renaming this variable to something else, since it collides with Go’s built-in max method (and similarly for min)?
| if len(c.items) == 0 { | ||
| return nil | ||
| } | ||
| rng := rand.New(rand.NewSource(time.Now().UnixNano())) |
There was a problem hiding this comment.
Initializing a random generator every time is slower. How about we initialize a global generator and reuse it? And why this method is returning a pointer of type T?
|
|
||
| func Reduce[T, R any](c *Collection[T], fn func(R, T, int) R, initial R) R { | ||
| result := initial | ||
| for i, item := range c.items { |
There was a problem hiding this comment.
How about we keep the input order as the index and use the remaining arguments for the fn(same for all the places where we pass index in function)?
| return -1 | ||
| } | ||
|
|
||
| func (c *Collection[T]) Shift() *T { |
There was a problem hiding this comment.
Why return a pointer to a generic type? If the user needs a pointer, they can simply assign T as a pointer type anyway, right? Can we fix similar issue in other methods also?
| shuffled := make([]T, len(c.items)) | ||
| copy(shuffled, c.items) | ||
|
|
||
| rng := rand.New(rand.NewSource(time.Now().UnixNano())) |
| start = len(c.items) | ||
| } | ||
|
|
||
| end := start + deleteCount |
There was a problem hiding this comment.
What if the user accidentally passes a negative deleteCount? It would end up duplicating the elements, right?
| for i, item := range c.items { | ||
| c.items[i] = fn(item, i) | ||
| } | ||
| return c |
There was a problem hiding this comment.
Why do we return the original collection in some places and create a new one in others? How about always returning a new collection so the user has fewer surprises? For example, Transform also modifies the original if that's the expected behavior, how about documenting it?
| func (c *Collection[T]) Union(other *Collection[T]) *Collection[T] { | ||
| existing := make(map[string]bool) | ||
| for _, item := range c.items { | ||
| existing[fmt.Sprintf("%v", item)] = true |
There was a problem hiding this comment.
This is very performance-heavy. For example, if T is a large User object, checking it this way will be significantly slower. In any case, we should not use the fmt package for key generation. For example:
type BigUser struct {
ID int
Name string
Bio string // Long text
Metadata map[string]string
}
// IF YOU USE fmt.Sprintf("%v", user):
// 1. Go has to reflectively walk through ID, Name, Bio, and the Map.
// 2. It allocates a massive string for EVERY user.
// 3. It compares massive strings.
// IF YOU USE a proper Key (User.ID):
// 1. Go compares one integer.
// 2. Zero allocation.
// 3. Instant.So maybe we can accept a keyFunc as well, right? Also, this issue exists in every method that checks for seen and uses fmt.
| } | ||
| } | ||
|
|
||
| func (c *Collection[T]) WhereIn(field string, values []interface{}) *Collection[T] { |
There was a problem hiding this comment.
Using this method will be a lot harder than expected because every time we use it we will need to convert the values to []any which a lot of pain, workaround would be to accept it like ...any.
There was a problem hiding this comment.
|
|
||
| switch operator { | ||
| case "=", "==": | ||
| return reflect.DeepEqual(*fieldValue, value) |
There was a problem hiding this comment.
There is an issue or inconsistent behaviour, for example:
func main() {
p := Product{Price: 100}
c := Of([]Product{p})
// "100" (int) > "50" (string) -> True
fmt.Println(c.Where("Price", ">", "50").Count()) // Output: 1 (Found)
// "100" (int) == "100" (string) -> False
fmt.Println(c.Where("Price", "=", "100").Count()) // Output: 0 (Not Found!)
}|
I recommend against merging methods that are ambiguous, handle edge cases incorrectly, or introduce significant performance bottlenecks. It is better to omit these features than to expose unsafe or unoptimized implementations. |
Yes, we recommend multiple small PRs instead of a big one. It's hard to review it. We can follow this next time. |
hwbrzzl
left a comment
There was a problem hiding this comment.
Approval
Approving this PR to merge the comprehensive Collection library implementation. 🎉
Why We're Merging
- Core functionality is solid - Provides 100+ Laravel-style collection methods with both eager and lazy evaluation
- Highly requested feature - Closes goravel/goravel#566 which has been pending
- Strong foundation - Generic implementation with comprehensive method coverage
- Team consensus - @hwbrzzl and @krishankumar01 both acknowledge the solid implementation
- Issues are non-blocking - All identified issues can be addressed in follow-up PRs
Follow-up Issue Created
All review comments, concerns, and improvement suggestions have been documented in:
goravel/goravel#883
This includes:
- Critical goroutine leak fixes
- Performance optimizations (Random generator, key generation)
- Behavioral consistency improvements
- API design enhancements
- Edge case handling
- Documentation improvements
- Test coverage expansion
Next Steps
The team can address issues in focused, reviewable PRs:
- Critical goroutine leaks (priority 1)
- Performance improvements (priority 2)
- API consistency fixes
- Enhanced test coverage
This approach allows the community to start using the Collection library while we iteratively improve it based on real-world feedback.
Thank you @ahmed3mar for this substantial contribution! 🙏
* chore: Update upgrade DB packages (#1374) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * Internalize file rotation logic from goravel/file-rotatelogs (#1375) * Initial plan * Implement internal file rotation to replace goravel/file-rotatelogs Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Improve cleanup logic with glob pattern matching and add comprehensive tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Address code review feedback: fix trailing whitespace, add cleanup wait, and make tests deterministic Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * optimize --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> Co-authored-by: Bowen <hwbrzzl@gmail.com> * chore: Update non-major dependencies (#1373) * chore: Update non-major dependencies * optimize * optimize * renovate/non-major-dependencies * optimize --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Bowen <hwbrzzl@gmail.com> * feat: [#726] Add HTTP server and client telemetry instrumentation [5] (#1326) * add http middleware * add http transport * optimise http telemetry package * add test cases for Telemetry middleware * add auto instrumentation configs * add config to enable Telemetry for http clients * add docs for config facade * add ConfigFacade nil warning * disable default telemetry * optimise transport * add kill switch for instrumentation * update the stubs * remove unnecessary handler * optimise log instrumentation * move route registration in the end * lazily initialize middleware and transport to work with new application_builder * optimise channel test * optimise channel test * accept telemetry facade as an input instead of using global instance * use a callback to resolve the telemetry facade instance * optimise grpc handler to remove usage of telemetry and config facade * optimise the grpc handler * use telemetry transport if enabled * optimise http auto instrumentation * optimize log test cases * optimise * optimise * optimise * optimise * revert PR#1357 * fix log test cases * revert GRPC changes * optimise middleware * remove zipkin trace driver * correct GRPC enable condition * correct http transport enable condition * correct log channel enable condition * update go mod * fix test cases * fix test cases * fix test cases * fix tests --------- Co-authored-by: Bowen <hwbrzzl@gmail.com> * chore: optimize runner tests for Windows (#1377) Co-authored-by: Bowen <hwbrzzl@github.com> * chore: Update non-major dependencies (#1378) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * fix: runner stuck (#1381) * fix: runner stuck * optimize * optimize * optimize * optimize * optimize * chore: Update non-major dependencies (#1382) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * feat: [#849] Add table support to the console context (#1380) * add table function in cli * add test cases for new method Table * update mocks * add column level styles * fix lint error * move style vars to new file * add GlobalHuhTheme * update go mod tidy * feat: [#546] artisan command up and down to set website Maintenance mode (#1198) * Add up and down command * Use file helper to create files and add unit tests * Fix the foundation.App dependency * Use support/path instead of foundation.App * Add unit test case * Add some missing checks * Add more missing checks * One more * Fix tmpfile path * Use T.TempDir instead of os.TempDir in tests * Add reason to the down command * Add option to the tests * Change the maintenance file name * close created file handles * Defer abort * Fix the linter issue * Add more options to the down command * Use options * Check for maintenance mode respond * Fix down_command_test * Add more unit test cases * Fix more lint issues * Fix lint issue * fix tests * fix tests * fix tests * fix tests * Address PR comments * Fix check_for_maintenance_test * Check Aborts in check_for_maintenance * Rename check_for_maintenance_mode * Fix and Write more unit tests for check_for_maintenance_mode middleware * fix down command * Fix down_command_test * Fix up_command --------- Co-authored-by: Bowen <hwbrzzl@gmail.com> * feat: Laravel-style Collection library (#1134) Merges comprehensive Collection library with 100+ methods for data manipulation. Implements both eager (Collection) and lazy (LazyCollection) evaluation strategies. Closes goravel/goravel#566 Follow-up improvements tracked in: goravel/goravel#883 * Increase route module coverage for factory, provider, and runner wiring paths (#1384) * Initial plan * test(route): cover route factory and service provider paths Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * optimize * optimize --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> Co-authored-by: Bowen <hwbrzzl@gmail.com> * chore: optimize interface (#1389) * Increase crypt module coverage by exercising AES key validation and failure paths (#1385) * Initial plan * test: add crypt AES edge-case coverage Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: improve readability of crypt key-length cases Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: address crypt PR feedback and simplify test structure Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: use configmock EXPECT style in TestNewAES Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: standardize config mock setup across aes tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Increase filesystem module test coverage for storage, service provider, and file facade paths (#1387) * Initial plan * Add filesystem application and provider coverage tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Polish filesystem coverage tests after review feedback Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Refine filesystem tests based on review feedback Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Apply review style suggestions in filesystem tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Increase mail module test coverage to 70%+ with focused unit tests (#1386) * Initial plan * test(mail): add focused unit coverage for application, options, and service provider Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): address review feedback on test specificity and readability Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): merge application unit tests and remove untyped mock matchers Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): deduplicate matchers and tighten queue job assertion Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): use AnythingOfType and any in application tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): derive bind callback type string from any signature Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): simplify bind callback type matcher Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): use AnythingOfType for template render expectations Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(mail): restore specific MatchedBy assertions per review clarification Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Increase hash module coverage with targeted tests for driver selection and service provider paths (#1388) * Initial plan * test: increase hash module coverage for driver and provider paths Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: cover hash provider and driver selection paths Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: address hash module review feedback Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: merge hash module tests into application_test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: use EXPECT API in hash service provider test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: use any alias in hash singleton callback test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: add AI instructions file (#1393) * Increase core framework coverage via targeted service provider and builder-path tests (#1391) * Initial plan * Add service provider coverage tests for core modules Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Expand core module coverage tests and validate suite Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Revert unintended test module dependency drift Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Address actionable PR review suggestions in coverage tests Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Finalize review feedback handling Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Revert unintended tests module dependency updates Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Tighten provider test matchers per review feedback Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * Increase infrastructure module coverage by adding Process/Packages/Log/Schedule tests and fixing errors.As target forwarding (#1392) * Initial plan * test(errors): cover As/Unwrap/Ignore/Join edge cases Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(errors): use wrapped error in As coverage test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module file changes Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: add coverage for process packages log schedule modules Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: refine schedule coverage tests per review Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module dependency updates Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: assert boot no-panic in log and process providers Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: finalize review feedback responses for boot test assertions Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module file changes Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: expand service-layer coverage across Event, Queue, gRPC, Cache, and Console (#1390) * Initial plan * test(event): cover service provider and queued listener dispatch paths Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: finalize event coverage validation Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module dependency updates Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test(event): address review suggestions for robust command matching and queue error path Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: finalize PR feedback follow-up Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module file updates Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: add focused coverage cases for queue grpc cache console Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: clarify queue nil args in empty chain test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module dependency updates Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * test: avoid nil context in console usage-error test Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: finalize lint ci fix validation Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: revert unintended tests module dependency drift Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: Update non-major dependencies (#1399) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> * chore: add generate flag to artisan build command (#1402) * feat: [#911] add query context accessor (#1401) * chore: optimize agents (#1396) * chore: optimize agents * optimize * address comments * Initial plan * fix: backport migrate rollback default handling to v1.17.x Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * chore: align branch tree with v1.17.x before backporting rollback fix Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> * optimize * optimize --------- Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> Co-authored-by: Bowen <hwbrzzl@gmail.com> Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com> Co-authored-by: hwbrzzl <24771476+hwbrzzl@users.noreply.github.com> Co-authored-by: krishan kumar <84431594+krishankumar01@users.noreply.github.com> Co-authored-by: Bowen <hwbrzzl@github.com> Co-authored-by: Mohan Raj <praem1990@gmail.com> Co-authored-by: Ahmed M. Ammar <ahmed3mar@outlook.com> Co-authored-by: 耗子 <haozi@loli.email> Co-authored-by: ALMAS <almas.cc@icloud.com>



📑 Description
Closes goravel/goravel#566
Basic Usage
Creating Collections
Basic Operations
Filtering and Mapping
Aggregation Methods
Conditional Operations
Available Methods
Core Methods (Alphabetical)
After(value)- Get item after given valueAll()- Get all items as sliceAverage(keyFunc)- Calculate average using key functionBefore(value)- Get item before given valueChunk(size)- Split into chunks of given sizeClone()- Create a copy of the collectionCollapse()- Collapse nested arrays into single arrayCombine(keys)- Combine with keys to create mapContains(value)- Check if collection contains valueCount()- Get item countDiff(other)- Get difference with another collectionEach(func)- Iterate over each itemEvery(predicate)- Check if all items match predicateFilter(predicate)- Filter items by predicateFirst()- Get first itemFlatten()- Flatten nested structuresGroupBy(keyFunc)- Group items by key functionIntersect(other)- Get intersection with another collectionIsEmpty()- Check if collection is emptyIsNotEmpty()- Check if collection is not emptyJoin(separator)- Join items with separatorLast()- Get last itemMap(func)- Transform each item with a function (returns Collection[interface{}])Merge(other)- Merge with another collectionPartition(predicate)- Split into two collections by predicatePluck(field)- Extract field valuesPush(items...)- Add items to endReverse()- Reverse orderSearch(value)- Find index of valueSlice(start, length)- Get slice of itemsSort(lessFunc)- Sort by comparison functionSortBy(keyFunc)- Sort by key functionSum(keyFunc)- Calculate sum using key functionTake(n)- Take first n itemsUnique()- Get unique itemsWhere(field, operator, value)- Filter by field comparisonZip(other)- Zip with another collectionWhere Method - Laravel-style Filtering
The
Wheremethod supports multiple patterns for flexible filtering:Supported Operators:
=,==- Equality!=- Inequality>,>=- Greater than, Greater than or equal<,<=- Less than, Less than or equallike- Case-insensitive substring matchnot like- Case-insensitive substring exclusionUtility Methods
Debug()- Print collection contentsDump()- Print collection contentsTap(func)- Execute function and return collectionToJSON()- Convert to JSON stringWhen(condition, func)- Execute function if condition is trueUnless(condition, func)- Execute function if condition is falseMap Method - Laravel-style Transformation
The
Mapmethod provides Laravel-style transformation capabilities:Generic Functions
Some operations require type transformation and are provided as generic functions for type safety:
Testing
go test -vExamples
See
example_test.gofor 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
Lazy Operations
Performance Benefits
Lazy vs Eager
Converting Between Collections
LazyCollection Methods
All()- Materialize all itemsCount()- Count itemsFilter(predicate)- Filter itemsMap(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 itemsSkip(n)- Skip first n itemsTakeWhile(predicate)- Take while condition is trueDropWhile(predicate)- Drop while condition is trueUnique()- Get unique itemsSort(lessFunc)- Sort itemsReverse()- Reverse orderSum(keyFunc)- Calculate sumAverage(keyFunc)- Calculate averageMin(keyFunc)- Find minimumMax(keyFunc)- Find maximumGroupBy(keyFunc)- Group itemsPartition(predicate)- Split into two collectionsFlatMap(func)- Flat map transformationEach(func)- Execute function for each itemForEach(func)- Execute function for each item (consumes collection)Iterator()- Get iterator for manual controlCollect()- Convert to eager CollectionLicense
MIT License
✅ Checks