-
-
Notifications
You must be signed in to change notification settings - Fork 699
Add solution for Challenge 27 by Johrespi #723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add solution for Challenge 27 by Johrespi #723
Conversation
WalkthroughAdds a Go generics utilities module providing Pair, Stack, Queue, Set types plus common slice utilities and a shared ErrEmptyCollection error for empty-collection operations (constructors, accessors, mutators, and set operations included). Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant C as Client
participant G as generics package
rect rgb(240,248,255)
note over C,G: Create and operate on collections
C->>G: NewStack[T]()
C->>G: Push(value)
C->>G: Pop()
alt empty
G-->>C: ErrEmptyCollection
else not empty
G-->>C: value
end
end
rect rgb(245,255,250)
C->>G: NewQueue[T]()
C->>G: Enqueue(value)
C->>G: Dequeue()
alt empty
G-->>C: ErrEmptyCollection
else not empty
G-->>C: value
end
end
rect rgb(255,250,240)
C->>G: NewSet[T]()
C->>G: Add(v)
C->>G: Union(s1,s2)
G-->>C: *Set[T]
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes
Possibly related PRs
Pre-merge checks✅ Passed checks (3 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
challenge-27/submissions/Johrespi/solution-template.go (1)
19-213: Clean up placeholder TODO comments.The functions are already implemented, so the remaining
// TODOmarkers are misleading. Please remove them (and similar ones throughout the file) to avoid implying unfinished work.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
♻️ Duplicate comments (1)
challenge-27/submissions/Johrespi/solution-template.go (1)
245-258: Handle nil set inputs.The
Intersectionfunction will panic if eithers1ors2is a zero-value Set with nil maps. Add nil checks before ranging.func Intersection[T comparable](s1, s2 *Set[T]) *Set[T] { - // TODO: Implement this function - s3 := NewSet[T]() - for k, _ := range s1.items { - if s2.Contains(k) { - s3.Add(k) + if s1 != nil && s1.items != nil && s2 != nil { + for k := range s1.items { + if s2.Contains(k) { + s3.Add(k) + } } } return s3 }
🧹 Nitpick comments (4)
challenge-27/submissions/Johrespi/solution-template.go (4)
88-96: Simplify to idiomatic Go.The IsEmpty method can be simplified to a single return statement.
func (s *Stack[T]) IsEmpty() bool { - // TODO: Implement this method - - if len(s.items) == 0 { - return true - } - - return false + return len(s.items) == 0 }
157-163: Simplify to idiomatic Go.The IsEmpty method can be simplified to a single return statement.
func (q *Queue[T]) IsEmpty() bool { - // TODO: Implement this method - if len(q.items) == 0 { - return true - } - return false + return len(q.items) == 0 }
206-210: Guard zero-value Set from panics.Calling
Sizeon a zero-value Set returns 0 without panicking (sincelen(nil)is 0), but for defensive programming and consistency with other methods, consider adding an explicit nil check.func (s *Set[T]) Size() int { - // TODO: Implement this method + if s.items == nil { + return 0 + } return len(s.items) }
339-355: Simplify boolean map check.The comparison at line 347 is verbose. In Go, map lookups return false for missing keys, so you can check the boolean value directly.
for _, e := range slice { - if m[e] == true { + if m[e] { continue } res = append(res, e) m[e] = true }
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
challenge-27/submissions/Johrespi/solution-template.go(1 hunks)
🔇 Additional comments (10)
challenge-27/submissions/Johrespi/solution-template.go (10)
5-6: LGTM!The sentinel error pattern is correctly implemented and provides clear semantics for empty collection operations.
12-28: LGTM!The Pair implementation is correct with proper generic type handling and the Swap method correctly inverts the type parameters.
34-79: LGTM!The Stack implementation correctly handles LIFO semantics with proper empty-collection error handling.
181-188: LGTM!The nil check for zero-value Set correctly addresses the previous review feedback.
278-289: LGTM!The Filter implementation correctly applies the predicate and builds the result slice.
291-300: LGTM!The Map implementation correctly applies the mapper function to transform elements.
302-311: LGTM!The Reduce implementation correctly accumulates values using the reducer function.
313-324: LGTM!The Contains implementation correctly checks for element presence in the slice.
326-337: LGTM!The FindIndex implementation correctly returns the index of the first occurrence or -1 if not found.
212-226: Guard zero-value Set from panics.Calling
Elementson a zero-value Set panics when ranging overs.itemsat line 221. Please add a nil check.func (s *Set[T]) Elements() []T { - // TODO: Implement this method - - if len(s.items) == 0 { + if s.items == nil || len(s.items) == 0 { return []T{} }Likely an incorrect or invalid review comment.
| // Dequeue removes and returns the front element from the queue | ||
| // Returns an error if the queue is empty | ||
| func (q *Queue[T]) Dequeue() (T, error) { | ||
| // TODO: Implement this method | ||
| var zero T | ||
|
|
||
| if len(q.items) == 0 { | ||
| return zero, ErrEmptyCollection | ||
| } | ||
|
|
||
| removed := q.items[0] | ||
| q.items = q.items[1:] | ||
|
|
||
| return removed, nil | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Memory leak: backing array retains removed elements.
The slice reslicing at line 131 (q.items = q.items[1:]) doesn't release the memory of dequeued elements. The backing array still holds references to removed items, preventing garbage collection. For long-lived queues, this can accumulate substantial memory.
Apply this diff to nil out the removed element before reslicing:
removed := q.items[0]
+ q.items[0] = zero // Release reference for GC
q.items = q.items[1:]Alternatively, for high-throughput queues, consider implementing a ring buffer or periodic slice reallocation when the queue becomes empty.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Dequeue removes and returns the front element from the queue | |
| // Returns an error if the queue is empty | |
| func (q *Queue[T]) Dequeue() (T, error) { | |
| // TODO: Implement this method | |
| var zero T | |
| if len(q.items) == 0 { | |
| return zero, ErrEmptyCollection | |
| } | |
| removed := q.items[0] | |
| q.items = q.items[1:] | |
| return removed, nil | |
| } | |
| // Dequeue removes and returns the front element from the queue | |
| // Returns an error if the queue is empty | |
| func (q *Queue[T]) Dequeue() (T, error) { | |
| // TODO: Implement this method | |
| var zero T | |
| if len(q.items) == 0 { | |
| return zero, ErrEmptyCollection | |
| } | |
| removed := q.items[0] | |
| q.items[0] = zero // Release reference for GC | |
| q.items = q.items[1:] | |
| return removed, nil | |
| } |
🤖 Prompt for AI Agents
In challenge-27/submissions/Johrespi/solution-template.go around lines 120 to
134, the Dequeue implementation reslices with q.items = q.items[1:] which leaves
the removed element referenced in the backing array and prevents GC; before
reslicing, assign the removed slot to the zero value (e.g., q.items[0] = zero)
to drop the reference, then reslice; for high-throughput scenarios consider
switching to a ring buffer or periodically copying remaining items into a new
slice when the head index grows large.
| // Remove removes an element from the set if it exists | ||
| func (s *Set[T]) Remove(value T) { | ||
| // TODO: Implement this method | ||
| delete(s.items, value) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard zero-value Set from panics.
Calling Remove on a zero-value Set panics because s.items is nil, similar to the issue fixed in Add. For consistency and safety, please initialize the map on demand.
func (s *Set[T]) Remove(value T) {
- // TODO: Implement this method
+ if s.items == nil {
+ return
+ }
delete(s.items, value)
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Remove removes an element from the set if it exists | |
| func (s *Set[T]) Remove(value T) { | |
| // TODO: Implement this method | |
| delete(s.items, value) | |
| } | |
| // Remove removes an element from the set if it exists | |
| func (s *Set[T]) Remove(value T) { | |
| if s.items == nil { | |
| return | |
| } | |
| delete(s.items, value) | |
| } |
🤖 Prompt for AI Agents
In challenge-27/submissions/Johrespi/solution-template.go around lines 190 to
194, calling Remove on a zero-value Set can panic because s.items may be nil;
guard against this by checking if s.items == nil and initializing it with
make(map[T]struct{}) before performing the delete so Remove becomes safe to call
on zero-value sets.
| // Contains returns true if the set contains the given element | ||
| func (s *Set[T]) Contains(value T) bool { | ||
| // TODO: Implement this method | ||
| _, exists := s.items[value] | ||
| if exists { | ||
| return true | ||
| } | ||
| return false | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard zero-value Set from panics.
Calling Contains on a zero-value Set panics because s.items is nil. Please add a nil check and simplify the return logic.
func (s *Set[T]) Contains(value T) bool {
- // TODO: Implement this method
- _, exists := s.items[value]
- if exists {
- return true
- }
- return false
+ if s.items == nil {
+ return false
+ }
+ return s.items[value]
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Contains returns true if the set contains the given element | |
| func (s *Set[T]) Contains(value T) bool { | |
| // TODO: Implement this method | |
| _, exists := s.items[value] | |
| if exists { | |
| return true | |
| } | |
| return false | |
| } | |
| // Contains returns true if the set contains the given element | |
| func (s *Set[T]) Contains(value T) bool { | |
| if s.items == nil { | |
| return false | |
| } | |
| return s.items[value] | |
| } |
🤖 Prompt for AI Agents
In challenge-27/submissions/Johrespi/solution-template.go around lines 196 to
204, Contains panics when called on a zero-value Set because s.items can be nil;
add a guard that returns false if s is nil or s.items is nil, and simplify the
logic to directly return the lookup result (e.g., perform the nil checks then
return exists).
| // Union returns a new set containing all elements from both sets | ||
| func Union[T comparable](s1, s2 *Set[T]) *Set[T] { | ||
| // TODO: Implement this function | ||
|
|
||
| s3 := NewSet[T]() | ||
|
|
||
| for k, _ := range s1.items { | ||
| s3.Add(k) | ||
| } | ||
|
|
||
| for k, _ := range s2.items { | ||
| s3.Add(k) | ||
| } | ||
|
|
||
| return s3 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle nil set inputs.
The Union function will panic if either s1 or s2 is a zero-value Set with nil maps. Add nil checks before ranging over the maps.
func Union[T comparable](s1, s2 *Set[T]) *Set[T] {
- // TODO: Implement this function
-
s3 := NewSet[T]()
- for k, _ := range s1.items {
- s3.Add(k)
+ if s1 != nil && s1.items != nil {
+ for k := range s1.items {
+ s3.Add(k)
+ }
}
- for k, _ := range s2.items {
- s3.Add(k)
+ if s2 != nil && s2.items != nil {
+ for k := range s2.items {
+ s3.Add(k)
+ }
}
return s3
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Union returns a new set containing all elements from both sets | |
| func Union[T comparable](s1, s2 *Set[T]) *Set[T] { | |
| // TODO: Implement this function | |
| s3 := NewSet[T]() | |
| for k, _ := range s1.items { | |
| s3.Add(k) | |
| } | |
| for k, _ := range s2.items { | |
| s3.Add(k) | |
| } | |
| return s3 | |
| } | |
| // Union returns a new set containing all elements from both sets | |
| func Union[T comparable](s1, s2 *Set[T]) *Set[T] { | |
| s3 := NewSet[T]() | |
| if s1 != nil && s1.items != nil { | |
| for k := range s1.items { | |
| s3.Add(k) | |
| } | |
| } | |
| if s2 != nil && s2.items != nil { | |
| for k := range s2.items { | |
| s3.Add(k) | |
| } | |
| } | |
| return s3 | |
| } |
🤖 Prompt for AI Agents
In challenge-27/submissions/Johrespi/solution-template.go around lines 228 to
243, the Union function ranges over s1.items and s2.items which will panic if s1
or s2 is a zero-value or has a nil items map; add nil-safe guards before
iterating: check if s1 != nil && s1.items != nil before the first range and
similarly for s2, so the function still returns a new empty set when either
input is nil without causing a runtime panic.
| // Difference returns a new set with elements in s1 that are not in s2 | ||
| func Difference[T comparable](s1, s2 *Set[T]) *Set[T] { | ||
| // TODO: Implement this function | ||
|
|
||
| s3 := NewSet[T]() | ||
|
|
||
| for k, _ := range s1.items { | ||
| if !s2.Contains(k) { | ||
| s3.Add(k) | ||
| } | ||
| } | ||
| return s3 | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle nil set inputs.
The Difference function will panic if either s1 or s2 is a zero-value Set with nil maps. Add nil checks before ranging.
func Difference[T comparable](s1, s2 *Set[T]) *Set[T] {
- // TODO: Implement this function
-
s3 := NewSet[T]()
- for k, _ := range s1.items {
- if !s2.Contains(k) {
- s3.Add(k)
+ if s1 != nil && s1.items != nil && s2 != nil {
+ for k := range s1.items {
+ if !s2.Contains(k) {
+ s3.Add(k)
+ }
}
}
+
return s3
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Difference returns a new set with elements in s1 that are not in s2 | |
| func Difference[T comparable](s1, s2 *Set[T]) *Set[T] { | |
| // TODO: Implement this function | |
| s3 := NewSet[T]() | |
| for k, _ := range s1.items { | |
| if !s2.Contains(k) { | |
| s3.Add(k) | |
| } | |
| } | |
| return s3 | |
| } | |
| // Difference returns a new set with elements in s1 that are not in s2 | |
| func Difference[T comparable](s1, s2 *Set[T]) *Set[T] { | |
| s3 := NewSet[T]() | |
| if s1 != nil && s1.items != nil && s2 != nil { | |
| for k := range s1.items { | |
| if !s2.Contains(k) { | |
| s3.Add(k) | |
| } | |
| } | |
| } | |
| return s3 | |
| } |
🤖 Prompt for AI Agents
In challenge-27/submissions/Johrespi/solution-template.go around lines 260 to
272, the Difference function can panic when s1 or s2 are zero-value sets with
nil maps; add nil checks at the top: if s1 == nil || s1.items == nil return
NewSet[T]() (nothing to iterate), and if s2 == nil || s2.items == nil set s2 =
NewSet[T]() (treat as empty set) before the for-range so you never range over a
nil map and behavior matches treating nil as an empty set.
Challenge 27 Solution
Submitted by: @Johrespi
Challenge: Challenge 27
Description
This PR contains my solution for Challenge 27.
Changes
challenge-27/submissions/Johrespi/solution-template.goTesting
Thank you for reviewing my submission! 🚀