Skip to content

Commit

Permalink
Merge pull request #65 from Turtel216/feature-run-length-encoding
Browse files Browse the repository at this point in the history
Feature run length encoding
  • Loading branch information
shahzadhaider1 authored Nov 28, 2024
2 parents 6e28c2a + 0939ae8 commit bbc471b
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 1 deletion.
18 changes: 17 additions & 1 deletion EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,22 @@ func main() {
}
```

### RunLengthEncode
```go
func main() {
encoded := strings.RunLengthEncode("aaabbbccc")
fmt.Println(encoded) // Output: 3a3b3c
}
```

### RunLengthDecode
```go
func main() {
decoded, _ := strings.RunLengthDecode("3a3b3c")
fmt.Println(decoded) // Output: aaabbbccc
}
```

### IsValidEmail
```go
func main() {
Expand Down Expand Up @@ -1078,4 +1094,4 @@ if err != nil {
}
// Output: bar
```
---
---
2 changes: 2 additions & 0 deletions readME.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ require github.com/kashifkhan0771/utils v0.3.0
- **Reverse**: Reverses the characters in a string.
- **CommonPrefix**: Finds the longest common prefix of a set of strings.
- **CommonSuffix**: Finds the longest common suffix of a set of strings.
- **RunLengthEncode** Encodes a string using Run-length-encoding.
- **RunLengthDecode** Decodes a string that has been encoded using Run-length-encoding. Ruturns the original string and an error if the encoding failed

### 9. Structs
- **CompareStructs**: Compares two structs and returns the differences between them.
Expand Down
77 changes: 77 additions & 0 deletions strings/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package strings

import (
"regexp"
"strconv"
"strings"

"golang.org/x/text/cases"
Expand Down Expand Up @@ -133,6 +134,82 @@ func CaesarEncrypt(input string, shift int) string {
return string(shifted)
}

// RunLengthEncode takes a string and returns its Run-Length Encoded
// representation or the original string if the encoding did not achieve any compression
func RunLengthEncode(input string) string {
if len(input) == 0 {
return ""
}

var encoded strings.Builder
runes := []rune(input)
count := 1

for i := 1; i < len(input); i++ {
if runes[i] == runes[i-1] {
count++
} else {
encoded.WriteRune(runes[i-1])
encoded.WriteString(strconv.Itoa(count))
count = 1
}
}

// Write the last character and its count
encoded.WriteRune(runes[len(runes)-1])
encoded.WriteString(strconv.Itoa(count))

// Return the original string if encoding doesn't save space
if encoded.Len() >= len(input) {
return input
}

return encoded.String()
}

// RunLengthDecode takes a Run-Length Encoded string and returns
// the decoded string or an error + the orignal encoded string
func RunLengthDecode(encoded string) (string, error) {
if len(encoded) == 0 {
return "", nil
}

var decoded strings.Builder
runes := []rune(encoded)
length := len(runes)
i := 0

for i < length {
char := runes[i]
j := i + 1

// Check if a number follows the character
for j < length && runes[j] >= '0' && runes[j] <= '9' {
j++
}

if j > i+1 {
// Parse the count if a number follows the character
count, err := strconv.Atoi(string(runes[i+1 : j]))

// If an error accures return the error and the original string
if err != nil {
return encoded, err
}

decoded.WriteString(strings.Repeat(string(char), count))
} else {
// If no number follows, treat the character as unencoded
decoded.WriteRune(char)
}

// Move to the next character group
i = j
}

return decoded.String(), nil
}

// CaesarDecrypt decrypts a string encrypted with the Caesar cipher and a given shift.
func CaesarDecrypt(input string, shift int) string {
return CaesarEncrypt(input, -shift)
Expand Down
72 changes: 72 additions & 0 deletions strings/strings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,78 @@ func TestRot13Decode(t *testing.T) {
}
}

func TestRunLengthEncode(t *testing.T) {
type args struct {
input string
}
tests := []struct {
name string
args args
want string
}{
{
name: "success - encode a string using Run-Length encoding",
args: args{input: "aaabbccccdd"},
want: "a3b2c4d2",
},
{
name: "success - make sure RunLengthEncode does not alter a normal string",
args: args{input: "abcdefg"},
want: "abcdefg",
},
{
name: "success - encode a string containing special characters using Run-Length encoding",
args: args{input: "!!!a#$$$$$@"},
want: "!3a1#1$5@1",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := RunLengthEncode(tt.args.input); got != tt.want {
t.Errorf("RunLengthEncode() = %v, want %v", got, tt.want)
}
})
}
}

func TestRunLengthDecode(t *testing.T) {
type args struct {
input string
}
tests := []struct {
name string
args args
want string
}{
{
name: "success - decode a string that has been encoded using Run-Length encoding",
args: args{input: "a3b2c4d2"},
want: "aaabbccccdd",
},
{
name: "success - decode a string that has been encoded using Run-Length encoding",
args: args{input: "A2B2C"},
want: "AABBC",
},
{
name: "success - decode a string that has been encoded using Run-Length encoding",
args: args{input: "a2z"},
want: "aaz",
},
{
name: "success - decode a string containing special characters that has been encoded using Run-Length encoding",
args: args{input: "!3#%$5"},
want: "!!!#%$$$$$",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got, _ := RunLengthDecode(tt.args.input); got != tt.want {
t.Errorf("RunLengthDecode() = %v, want %v", got, tt.want)
}
})
}
}
func TestCaesarEncrypt(t *testing.T) {
type args struct {
input string
Expand Down

0 comments on commit bbc471b

Please sign in to comment.