Since that AtBash Cipher is a simple reverse, you don't need both _plain
and _cipher
. There is no reason for _indices
to be at the class level; it should instead be locally defined in both Encode
and Decode
.
You may even consider doing away with having a class instance and instead make the class and its methods static. If you keep as a class instance, Decode
could be simplified to simply returning _input
!!!
You should be sure to set the input string to lower case in case someone enters "Hello". Something like:
_input = input.ToLowerInvariant();
Except you should try to avoid the Turkish "ll" issue of surrogate pairs to be treated as single character. The way to avoid is to use upper case.
Also the Splitter should just use the char[] and not convert ToList(). Then again, you don't need really Splitter.
Those are all things to think about. Here's my take on it:
public static class AtbashCipher
{
private const string DefaultAlphabet = "abcdefghijklmnopqrstuvwxyz";
public static string Encode(string input, string alphabet = DefaultAlphabet)
{
var plain = alphabet.ToUpperInvariant();
var letters = new List<char>(input.Length);
foreach (var c in input.ToUpperInvariant())
{
if (char.IsLetter(c))
{
// Get index of plain alphabet, but subtract from end
var index = plain.Length - plain.IndexOf(c) - 1;
letters.Add(plain[index]);
}
}
return new string(letters.ToArray());
}
public static string Decode(string input, string alphabet = DefaultAlphabet)
{
// reverse alphabet to get cipher
string cipher = new string(alphabet.ToUpperInvariant().Reverse().ToArray());
var letters = new List<char>(input.Length);
foreach (var c in input.ToUpperInvariant())
{
if (char.IsLetter(c))
{
var index = cipher.Length - cipher.IndexOf(c) - 1;
letters.Add(cipher[index]);
}
}
return new string(letters.ToArray());
}
}
You may notice that Encode and Decode look very similar except for whether plain or cipher is chosen. In the name of DRY (Don't Repeat Yourself), this can be reduced to:
public static class AtbashCipher
{
private const string DefaultAlphabet = "abcdefghijklmnopqrstuvwxyz";
public static string Encode(string input, string alphabet = DefaultAlphabet)
{
var plain = alphabet.ToUpperInvariant();
return Transform(input, plain);
}
public static string Decode(string input, string alphabet = DefaultAlphabet)
{
var cipher = new string(alphabet.ToUpperInvariant().Reverse().ToArray());
return Transform(input, cipher);
}
private static string Transform(string input, string code)
{
var letters = new List<char>(input.Length);
foreach (var c in input.ToUpperInvariant())
{
if (char.IsLetter(c))
{
var index = code.Length - code.IndexOf(c) - 1;
letters.Add(code[index]);
}
}
return new string(letters.ToArray());
}
}
AtbashCipher
instance for each encode or decode operation. However, there is no need to keep any state around, and since encoding and decoding produce the same results, a single (static)string Encode(string input)
method should be sufficient. You can also calculate the substitute character instead of performing index-of searches, as Siegen demonstrates, so you don't need those lookup strings anymore (it should also be faster). \$\endgroup\$