Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions docs/exercises.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,14 @@ title: Exercises
<li>25_range_iterators - Range over Iterators</li>
<li>26_errors - Error Handling</li>
<li>27_custom_errors - Custom Errors</li>
<li>28_defer - Defer</li>
<li>29_go_routines - Go Routines</li>
<li>30_channels - Channels</li>
<li>31_mutexes - Mutexes</li>
<li>32_sorting - Sorting</li>
<li>33_string_formatting - String Formatting</li>
<li>34_channel_buffering - Channel Buffering</li>
<li>41_regex - Regular Expressions</li>
<li>36_json - JSON Processing</li>
<li>37_xml - XML Processing</li>
</ul>
Expand Down
22 changes: 17 additions & 5 deletions internal/exercises/catalog.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,11 @@ concepts:
test_regex: ".*"
hints:
- Use `make(chan T, N)` to create a buffered channel and `<-` to send and receive on it.
- slug: 35_channel_sync
title: Channel Synchronization
test_regex: ".*"
hints:
- Use a buffered boolean channel and wait till go routine completes.
- slug: 39_channel_directions
title: Channel Directions
test_regex: ".*"
Expand All @@ -178,11 +183,18 @@ concepts:
- Send 50 jobs to the worker pool.
- Receive results from the worker pool and store them in a `rs` slice.
- Enforce type safety by specifying the channel directions in worker function.
- slug: 35_channel_sync
title: Channel Synchronization
test_regex: ".*"
hints:
- Use a buffered boolean channel and wait till go routine completes.
- slug: 41_regex
title: Regular Expressions
test_regex: ".*"
hints:
- Use regexp.MustCompile to create compiled regular expressions.
- Use MatchString to test if a string matches a pattern.
- Use FindAllString to extract all matches from text.
- Use ReplaceAllString to replace matches with new text.
- "Email regex: ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
- "Phone regex: ^\\(\\d{3}\\) \\d{3}-\\d{4}$"
- "Number regex: \\d+\\.?\\d*"
- "Vowel regex: [aeiouAEIOU]"
- slug: 37_xml
title: XML Encoding and Decoding
test_regex: ".*"
Expand Down
35 changes: 35 additions & 0 deletions internal/exercises/solutions/41_regex/regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package regex

import "regexp"

// IsValidEmail returns true if the email address is valid.
func IsValidEmail(email string) bool {
pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
re := regexp.MustCompile(pattern)
return re.MatchString(email)
}

// ExtractNumbers returns all numbers found in the input string.
func ExtractNumbers(text string) []string {
pattern := `\d+\.?\d*`
re := regexp.MustCompile(pattern)
matches := re.FindAllString(text, -1)
if matches == nil {
return []string{}
}
return matches
}

// ReplaceVowels replaces all vowels with asterisks.
func ReplaceVowels(text string) string {
pattern := `[aeiouAEIOU]`
re := regexp.MustCompile(pattern)
return re.ReplaceAllString(text, "*")
}

// IsPhoneNumber returns true if the phone number matches format (XXX) XXX-XXXX.
func IsPhoneNumber(phone string) bool {
pattern := `^\(\d{3}\) \d{3}-\d{4}$`
re := regexp.MustCompile(pattern)
return re.MatchString(phone)
}
37 changes: 37 additions & 0 deletions internal/exercises/templates/41_regex/regex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package regex

// Task:
// Implement regular expression functions using Go's regexp package.
//
// 1. Implement IsValidEmail to validate email addresses using regex.
// 2. Implement ExtractNumbers to extract all numbers from a string.
// 3. Implement ReplaceVowels to replace all vowels with asterisks.
// 4. Implement IsPhoneNumber to validate phone numbers in format (XXX) XXX-XXXX.

// IsValidEmail should return true if the email address is valid.
// A valid email should have the format: [email protected]
func IsValidEmail(email string) bool {
// TODO: implement using regexp.MustCompile and MatchString
return false
}

// ExtractNumbers should return all numbers found in the input string.
// Numbers can be integers or decimals (e.g., "123", "45.67").
func ExtractNumbers(text string) []string {
// TODO: implement using regexp.FindAllString
return nil
}

// ReplaceVowels should replace all vowels (a, e, i, o, u) with asterisks.
// Case-insensitive replacement.
func ReplaceVowels(text string) string {
// TODO: implement using regexp.MustCompile and ReplaceAllString
return ""
}

// IsPhoneNumber should return true if the phone number matches format (XXX) XXX-XXXX.
// Example: (123) 456-7890
func IsPhoneNumber(phone string) bool {
// TODO: implement using regexp.MustCompile and MatchString
return false
}
99 changes: 99 additions & 0 deletions internal/exercises/templates/41_regex/regex_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package regex

import "testing"

func TestIsValidEmail(t *testing.T) {
tests := []struct {
email string
expected bool
}{
{"[email protected]", true},
{"[email protected]", true},
{"invalid-email", false},
{"@domain.com", false},
{"user@", false},
{"user@domain", false},
{"", false},
}

for _, test := range tests {
result := IsValidEmail(test.email)
if result != test.expected {
t.Errorf("IsValidEmail(%q) = %v, want %v", test.email, result, test.expected)
}
}
}

func TestExtractNumbers(t *testing.T) {
tests := []struct {
text string
expected []string
}{
{"I have 123 apples and 45.67 oranges", []string{"123", "45.67"}},
{"The price is $99.99", []string{"99.99"}},
{"No numbers here", []string{}},
{"123", []string{"123"}},
{"12.34.56", []string{"12.34", "56"}},
{"", []string{}},
}

for _, test := range tests {
result := ExtractNumbers(test.text)
if len(result) != len(test.expected) {
t.Errorf("ExtractNumbers(%q) length = %d, want %d", test.text, len(result), len(test.expected))
continue
}
for i, num := range result {
if num != test.expected[i] {
t.Errorf("ExtractNumbers(%q)[%d] = %q, want %q", test.text, i, num, test.expected[i])
}
}
}
}

func TestReplaceVowels(t *testing.T) {
tests := []struct {
text string
expected string
}{
{"hello", "h*ll*"},
{"HELLO", "H*LL*"},
{"Hello World", "H*ll* W*rld"},
{"bcdfg", "bcdfg"},
{"", ""},
{"aeiou", "*****"},
{"AEIOU", "*****"},
}

for _, test := range tests {
result := ReplaceVowels(test.text)
if result != test.expected {
t.Errorf("ReplaceVowels(%q) = %q, want %q", test.text, result, test.expected)
}
}
}

func TestIsPhoneNumber(t *testing.T) {
tests := []struct {
phone string
expected bool
}{
{"(123) 456-7890", true},
{"(555) 123-4567", true},
{"(000) 000-0000", true},
{"123-456-7890", false},
{"(123)456-7890", false},
{"(123) 4567890", false},
{"123 456 7890", false},
{"(123) 456-789", false},
{"(12) 456-7890", false},
{"", false},
}

for _, test := range tests {
result := IsPhoneNumber(test.phone)
if result != test.expected {
t.Errorf("IsPhoneNumber(%q) = %v, want %v", test.phone, result, test.expected)
}
}
}