using System.Collections.Generic;
using System.Linq;
namespace Algorithms.Encoders
{
public class SoundexEncoder
{
public string Encode(string text)
{
text = text.ToLowerInvariant();
var chars = OmitHAndW(text);
IEnumerable<int> numbers = ProduceNumberCoding(chars);
numbers = CollapseDoubles(numbers);
numbers = OmitVowels(numbers);
numbers = CollapseLeadingDigit(numbers, text[0]);
numbers = numbers.Take(3);
numbers = PadTo3Numbers(numbers);
var final = numbers.ToArray();
return $"{text.ToUpperInvariant()[0]}{final[0]}{final[1]}{final[2]}";
}
private IEnumerable<int> CollapseLeadingDigit(IEnumerable<int> numbers, char c)
{
using var enumerator = numbers.GetEnumerator();
enumerator.MoveNext();
if (enumerator.Current == MapToNumber(c))
{
enumerator.MoveNext();
}
do
{
yield return enumerator.Current;
}
while (enumerator.MoveNext());
}
private IEnumerable<int> PadTo3Numbers(IEnumerable<int> numbers)
{
using var enumerator = numbers.GetEnumerator();
for (var i = 0; i < 3; i++)
{
yield return enumerator.MoveNext()
? enumerator.Current
: 0;
}
}
private IEnumerable<int> OmitVowels(IEnumerable<int> numbers) => numbers.Where(i => i != 0);
private IEnumerable<char> OmitHAndW(string text) => text.Where(c => c != 'h' && c != 'w');
private IEnumerable<int> CollapseDoubles(IEnumerable<int> numbers)
{
var previous = int.MinValue;
foreach (var i in numbers)
{
if (previous != i)
{
yield return i;
previous = i;
}
}
}
private IEnumerable<int> ProduceNumberCoding(IEnumerable<char> text) => text.Select(MapToNumber);
private int MapToNumber(char ch)
{
var mapping = new Dictionary<char, int>
{
['a'] = 0,
['e'] = 0,
['i'] = 0,
['o'] = 0,
['u'] = 0,
['y'] = 0,
['h'] = 8,
['w'] = 8,
['b'] = 1,
['f'] = 1,
['p'] = 1,
['v'] = 1,
['c'] = 2,
['g'] = 2,
['j'] = 2,
['k'] = 2,
['q'] = 2,
['s'] = 2,
['x'] = 2,
['z'] = 2,
['d'] = 3,
['t'] = 3,
['l'] = 4,
['m'] = 5,
['n'] = 5,
['r'] = 6,
};
return mapping[ch];
}
}
}