String Encoder with lower-case output Question: Take an input string and output the "encoded" string by using the following rules: 


 [1] vowels are replaced with number: a-&gt;1, e-&gt;2, etc
 [2] consonants are replaced with previous letter b-&gt;a, c-&gt;b, etc
 [3] y goes to space
 [4] space goes to y
 [5] numbers are reversed
 [6] other characters remain unchanged(punctuation, etc.)
 [7] all output should be lower case
 E.g.: Hello World! =&gt; g2kk4yv4qkc!



My Solution:

  /// pre processed conversions for letters
  private static Dictionary&lt;char, char&gt; Convert;

  public static string encode(string stringToEncode) {

     // approach: pre process a mapping (dictionary) for letter conversions
     // use a Dict for fastest look ups.  The first run, will take a little
     // extra time, subsequent usage will perform even better
     if (Convert == null || Convert.Count == 0) BuildConversionMappings();

     // our return val (efficient Appends)
     StringBuilder sb = new StringBuilder(); 

     // used for reversing the numbers
     Stack&lt;char&gt; nums = new Stack&lt;char&gt;(); 

     // iterate the input string
     for(int i = 0; i &lt; stringToEncode.Length; i++) {

        char c = stringToEncode[i]; 

        // we have 3 cases:
        // 1) is alpha ==&gt; convert using mapping
        // 2) is number ==&gt; peek ahead to complete the number
        // 3) is special char / punctunation ==&gt; ignore

        if(Convert.ContainsKey(c)) {
           sb.Append(Convert[c]);
           continue;
        }

        if(Char.IsDigit(c)) {
           nums.Push(c);

           // we've reached the end of the input string OR
           // we've reached the end of the number
           if (i == stringToEncode.Length - 1
              || !Char.IsDigit(stringToEncode[i + 1])) {
              while (nums.Count &gt; 0) {
                 sb.Append(nums.Pop());
              }
           }

           continue;
        }

        // not letter, not digit
        sb.Append(c);
     }
     return sb.ToString();
  }

  // create our mappings for letters
  private static void BuildConversionMappings() {

     Convert = new Dictionary&lt;char, char&gt;();

     // only loop once for both
     for(char c = 'B'; c&lt;='Z'; c++) {
        // add capitals version
        char val = (char)(c - 1);
        val = Char.ToLower(val);
        Convert.Add(c, val);
        // add lower case version
        Convert.Add(Char.ToLower(c),val);
     }

     // special cases
     Convert['y'] = ' ';
     Convert['Y'] = ' ';
     Convert.Add(' ', 'y');

     // vowels
     char[] vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
     for(int i = 0;i &lt; vowels.Length;i++) {
        var letter = vowels[i];
        var value = (i+1).ToString()[0];
        Convert[letter] = value;
        Convert[Char.ToUpper(letter)] = value;
     }
  }


I was asked this question for an interview and the only feedback I received was building a dictionary was a very poor choice and they were no longer interested in continuing the application process.  I was surprised and disappointed, but I want to improve for the next time.  What should I do better?
There are couple of things that might have been done better...


Initilize the dictionary form a static constructor or call the method to initialize the field. The method should not access it internally but return a dictionary as a result. The field itself should be readonly.
foreach could be used to iterate the stringToEncode.
Names of internal variables could be better:


Convert sounds like a method name. The dictionary should be named Conversions.
nums should be digits
sb should be called encoded

TryGetValue could have been used instead of ContainsKey
The dictionary is case-insensitive only because you put all letters there. Instead you should use the StringComparer.OrdinalIgnoreCase and make the dictionary &lt;string, char&gt; but this would be inconvenient. You could create your own IEqualityComparer&lt;char&gt;. This will cut in half its size .
I wish more var (optional)
I wish more LINQ


Things that are already good:


I find using a dictionary was a good choice.
The use of a Stack to reverse the order is very clever.
Using StringBuilder for efficiency is definitely a good choice either.




When we apply all suggestions the code could look like this:

Creating the IReadOnlyDictionary which returns a result and uses a couple of additional helper variables. No for loops.

// pre processed conversions for letters
private static readonly IReadOnlyDictionary&lt;char, char&gt; Conversions = BuildConversionDictionary();

private static IReadOnlyDictionary&lt;char, char&gt; BuildConversionDictionary()
{
    var conversions = new Dictionary&lt;char, char&gt;(CaseInsensitiveCharComparer);

    var alphabet = Enumerable.Range('a', 'z' - 'a' + 1).Select(x =&gt; (char)x);
    var vowels = new char[] { 'a', 'e', 'i', 'o', 'u' };
    var consonants = alphabet.Except(vowels);

    // consonants are replaced with previous letter b-&gt;a, c-&gt;b, etc
    foreach (var c in consonants)
    {
        conversions.Add(c, (char)(c - 1));
    }

    // y goes to space
    // space goes to y
    conversions['y'] = ' ';
    conversions[' '] = 'y';

    // vowels are replaced with number: a-&gt;1, e-&gt;2, etc
    foreach (var (c, i) in vowels.Select((c, i) =&gt; (c, i + 1)))
    {
        conversions.Add(c, (char)('0' + i));
    }

    return conversions;
}


The alternative equality comparer (here I'm using a helper-factory from my libraries):

private static IEqualityComparer&lt;char&gt; CaseInsensitiveCharComparer =
    EqualityComparerFactory&lt;char&gt;.Create
    (
        (x, y) =&gt; StringComparer.OrdinalIgnoreCase.Equals(x.ToString(), y.ToString()),
        obj =&gt; StringComparer.OrdinalIgnoreCase.GetHashCode(obj.ToString())
    );


The refactored encode (which should be Encode). What has changed here is that I first check for digits. If it's not one and we have some then dump the stack to the builder and clear it, othewise do the rest. Below the loop we have to dump it once again in case there are some digits left at the end.

public static string Encode(string stringToEncode)
{
    if (string.IsNullOrEmpty(stringToEncode)) throw new ArgumentException(nameof(stringToEncode));

    // our return val (efficient Appends)
    var encoded = new StringBuilder();

    // used for reversing the numbers
    var digits = new Stack&lt;char&gt;();

    // iterate the input string
    foreach (var c in stringToEncode)
    {
        if (char.IsDigit(c))
        {
            digits.Push(c);
            continue;
        }

        if (digits.Any())
        {
            encoded.Append(digits.ToArray());
            digits.Clear();
        }

        if (Conversions.TryGetValue(c, out var converted))
        {
            encoded.Append(converted);
            continue;
        }

        // something else, undefined
        encoded.Append(c);
    }

    // "dump" what's left
    encoded.Append(digits.ToArray());

    return encoded.ToString();
}




I think you cannot do anything else having only 60 minutes.