Skip to content
Open
Show file tree
Hide file tree
Changes from 5 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)
}
}
}