Duplicate Encoder
Tags: kata, challenge, codewars, csharp
Published: 2019-12-01 23:02:32

## Task description

> The goal of this exercise is to convert a string to a new string where each character in the new string is "(" if that character appears only once in the original string, or ")" if that character appears more than once in the original string. Ignore capitalization when determining if a character is a duplicate.
> 
> *Examples:*
> "din"      =>  "((("
> "recede"   =>  "()()()"
> "Success"  =>  ")())())"
> "(( @"     =>  "))((" 

## Task solution

### Tests

As I will be using [the `C#` language](https://docs.microsoft.com/en-us/dotnet/csharp/index) for this implementation, we will use the `Microsoft.VisualStudio.TestTools.UnitTesting` namespace which will provide testing utilities to us out of the box. For the tests we will use the examples provided in the task description above like so:

```csharp
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Kata;

namespace KataTests.UnitTests 
{
    [TestClass]
    public class DuplicateEncoderTests
    {
        [TestMethod]
        public void TaskExamplesPassAsExpected()
        {
            var encoder = new DuplicateEncoder();
            Assert.AreEqual("(((", encoder.Encode("din"));
            Assert.AreEqual("()()()", encoder.Encode("recede"));
            Assert.AreEqual(")())())", encoder.Encode("Success"));
            Assert.AreEqual("))((", encoder.Encode("(( @"));
        }
    }
}
```

### Implementation

```csharp
using System;
using System.Collections.Generic;

namespace Kata {
   public class DuplicateEncoder {
    private Dictionary<char, int> CharCounts(string text) {
      var letters = text.ToCharArray(); 
      var charCounts = new Dictionary<char, int>();

      foreach(char letter in text) {
         var lower_letter = Char.ToLower(letter);
         if (charCounts.ContainsKey(lower_letter)) charCounts[lower_letter]++;
         else charCounts.Add(lower_letter, 1);
      }

      return charCounts;
    }
    
    private string EncodeWordBasedOnCharCount(
      Dictionary<char, int> charCounts, 
      string text
    ) {
      var return_string = "";

      foreach(char letter in text) {
         var lower_letter = Char.ToLower(letter);
         if(charCounts[lower_letter] == 1) return_string += "(";
         else return_string += ")";
      }

      return return_string;
    }
    
    public string Encode(string text) {
      var charCounts = this.CharCounts(text);
      return this.EncodeWordBasedOnCharCount(charCounts, text);
    }
  }
}
```

The `DuplicateEncoder` class in the `Kata` namespace exposes 1 function publicly, that being the `Encode` function. This function will take in the `word` to encode and return the encoded string in response. Further to this, there are 2 private functions:

1. `CharCounts` takes the `word` to encode and generates a `Dictionary<char, int>` of its characters
2. `EncodeWordBasedOnCharCount` takes the character counts from `CharCounts` and the `word` to encode and return the encoded string

When we run the `Encode` function we will first generate a `Dictionary<char, int>` to represent the count of each character by calling the `CharCounts` function. From there we generate the encoded string based on the character counts and the `word` we wish to encode by calling the `EncodeWordBasedOnCharCount` function, for example we can express this with the following overview of that process:

```text
var encoder = new DuplicateEncoder();
encoder.Encode("test")
  -> `CharCounts("test")` -> `{t: 2, e: 1, s: 1 }`
  -> `EncodeWordBasedOnCharCount({t: 2, e: 1, s: 1 }, "test")` -> ")(()"
```

That's all we need to encode any string based on the task description. 😄

## Conclusions

I am getting back into C# lately and really like how simple its gotten since I last used it over 6 ish years back 😅. I would like to potentially refactor the solution at some point but for now I am quite happy with the tests and implementation. Let me know if you have any suggestions on improvements since I am looking to brush up my C# skills further anyway, otherwise, see you in the next one!