Skip to content

Conversation

@Johrespi
Copy link
Contributor

@Johrespi Johrespi commented Nov 8, 2025

Challenge 27 Solution

Submitted by: @Johrespi
Challenge: Challenge 27

Description

This PR contains my solution for Challenge 27.

Changes

  • Added solution file to challenge-27/submissions/Johrespi/solution-template.go

Testing

  • Solution passes all test cases
  • Code follows Go best practices

Thank you for reviewing my submission! 🚀

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 8, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Generic collections & utilities
challenge-27/submissions/Johrespi/solution-template.go
Adds package generics implementing ErrEmptyCollection, Pair[T,U] (NewPair, Swap), Stack[T] (NewStack, Push, Pop, Peek, Size, IsEmpty), Queue[T] (NewQueue, Enqueue, Dequeue, Front, Size, IsEmpty), Set[T comparable] (NewSet, Add, Remove, Contains, Size, Elements) plus Union, Intersection, Difference and slice helpers: Filter, Map, Reduce, Contains, FindIndex, RemoveDuplicates.

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
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • Boundary/error behavior: Pop / Dequeue / Peek / Front return ErrEmptyCollection consistently.
    • Correctness of Stack (LIFO) and Queue (FIFO) semantics.
    • Set implementation correctness (map-backed), and correctness of Union/Intersection/Difference.
    • Generic function correctness (type constraints and behavior for Filter/Map/Reduce/RemoveDuplicates).

Possibly related PRs

Pre-merge checks

✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title directly reflects the PR's main purpose: adding a Challenge 27 solution submission by the author Johrespi, which matches the changeset.
Description check ✅ Passed The description is relevant to the changeset, explaining that it contains a Challenge 27 solution with the file path and testing claims aligned with the actual changes.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 // TODO markers are misleading. Please remove them (and similar ones throughout the file) to avoid implying unfinished work.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2d4b9cf and b8c3916.

📒 Files selected for processing (1)
  • challenge-27/submissions/Johrespi/solution-template.go (1 hunks)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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 Intersection function will panic if either s1 or s2 is 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 Size on a zero-value Set returns 0 without panicking (since len(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

📥 Commits

Reviewing files that changed from the base of the PR and between b8c3916 and e7453d2.

📒 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 Elements on a zero-value Set panics when ranging over s.items at 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.

Comment on lines +120 to +134
// 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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
// 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.

Comment on lines +190 to +194
// 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)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
// 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.

Comment on lines +196 to +204
// 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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

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.

Suggested change
// 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).

Comment on lines +228 to +243
// 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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
// 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.

Comment on lines +260 to +272
// 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
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

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.

Suggested change
// 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.

@RezaSi RezaSi merged commit 4178ed3 into RezaSi:main Nov 10, 2025
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants