"Interview" problems

I've recently watched the Pluralsight course about job interviews. I've decided I will do some deeper analysis for one of a given problem. Although it seems to have a simple solution, I'll try to make things more complicated :)

Word score

Write the string scoring algorithm.
F = 3, J = 6, X = 12, A,I,E,O = 2, T = 5, other letters = 0.
Score is sum of letter scores.
Example 1: "XRay Machine" -> 12 + 0 + 2 + 0 + 0 + 0 + 2 + 0 + 0 + 2 + 0 + 2 = 20
Example 2: "Jabbt" -> 6 + 2 + 0 + 0 + 5 = 13

For each solution I will use a routine that iterates through input string:

var scores = stringToScore //"string"  
                .ToList() //"s","t","r","i","n","g"
                .Select(LetterScore) // 0, 5, ...
                .Sum();
If-based solution

So let's start with simplest "if" based solution (assume that input is in letter variable):

 private static int LetterScore(char letter)
{
    letter = char.ToUpper(letter);
    if (letter == 'A' || letter == 'I'
       || letter == 'E' || letter == 'O') 
       return 2;
    if (letter == 'F') return 3;
    if (letter == 'J') return 6;
    if (letter == 'X') return 12;
    if (letter == 'T') return 5;
    return 0;
}

Simple, rather short, and not flexible at all. But simplest solution can be sometimes the best solution, right?

Switch-based solution

We can convert our ifs to single switch statement.

private static int LetterScore(char letter)  
{
    letter = char.ToUpper(letter);
    switch (letter)
    {
        case 'A':
        case 'I':
        case 'E':
        case 'O':
            return 2;
        case 'F':
            return 3;
        case 'J':
            return 6;
        case 'X':
            return 12;
        case 'T':
            return 5;
    }
    return 0;
}

Few more cases and it becomes too long, but looks a little bit better. Still - same disadventages as ifs.

Dictionary-based solution

Instead of creating branches, let's save (character,its score) pairs in a dictionary, and try to get value for each letter at runtime.

IDictionary<char, int> _letterScores  
    = new Dictionary<char, int>()
    {
        { 'A', 2 },
        { 'I', 2 },
        { 'E', 2 },
        { 'O', 2 },
        { 'F', 3 },
        { 'J', 6 },
        { 'X', 12 },
        { 'T', 5 },
    };
    //...
private int LetterScore(char letter)  
{
    letter = char.ToUpper(letter);
    int letterScore;
    return (_letterScores.TryGetValue(letter, out letterScore)) 
                ? letterScore : 0;
}

This solution is by far more flexible. You can easily add another pair, or change value. This can be done at runtime too! But this time, each call will cause searching in the _letterScores dictionary, so performance will be worse.

Rules collection-based solution

Shortly speaking: make a list, fill it with handlers, find apropriate handler, get score for a letter.

interface IRuleHandler  
{
    bool CanHandle(char letter);
    int Handle(char letter);
}

//initialization:
private readonly IList<IRuleHandler>  _scoringHandlers  
    = new List<IRuleHandler>()
    {
        //Rule Handler is Lambda Based IRuleHandler implementation
        new RuleHandler(c => c=='A' || c=='I' || c=='E' || c=='O', c => 2),
        new RuleHandler(c => c=='F', c => 3),
        new RuleHandler(c => c=='J', c => 6),
        new RuleHandler(c => c=='X', c => 12),
        new RuleHandler(c => c=='T', c => 5),
        new RuleHandler(c => true, c => 0),
    };
// ...
private int LetterScore(char letter)  
{
    letter = char.ToUpper(letter);
    //find 1st that can handle, and get score
    return _scoringHandlers
                .First(handler => handler.CanHandle(letter))
                .Handle(letter);
}

Searching that way is more complex than in dictionary. If we want to get a score for a non-letter, or letter with score 0, we have to check whole collection. On the other hand, that solution is pretty elegant and points to Chain Of Responsibility pattern. Runtime management is possible, compile-time optimalisation does not. But premature optimisation is bad, right?

Full code along with unit tests is available on github