6

I have the following code which I would like to see as a oneliner. However, since I am very new to C#, I currently have no clue on how to do this...

Code:

static string ROT13 (string input)
{
    if (string.IsNullOrEmpty(input)) return input;

    char[] buffer = new char[input.Length];

    for (int i = 0; i < input.Length; i++)
    {
        char c = input[i];
        if (c >= 97 && c <= 122)
        {
            int j = c + 13;
            if (j > 122) j -= 26;
            buffer[i] = (char)j;
        }
        else if (c >= 65 && c <= 90)
        {
            int j = c + 13;
            if (j > 90) j -= 26;
            buffer[i] = (char)j;
        }
        else
        {
            buffer[i] = (char)c;
        }
    }
    return new string(buffer);
}

I am sorry for any inconvenience, just trying to learn more about this pretty language :)

RFerwerda
  • 1,237
  • 2
  • 10
  • 23
  • 7
    Just place all characters in your method on one line. – Hamlet Hakobyan Sep 11 '13 at 10:48
  • 2
    Any reason you want to intentionally make things unreadable? – Moo-Juice Sep 11 '13 at 10:57
  • 3
    Completely unrelated, but another thing you might want to learn is that [magic numbers](http://en.wikipedia.org/wiki/Magic_number_(programming)#Unnamed_numerical_constants) are usually not a good thing to have. As Save has shown, it's better to use the characters instead of their numerical values to better show what the code does. As a rule of thumb, any code is read more than ten times as much as it is written. So your goal should always be to write code as clear and understandable as possible and not to use as many clever tricks as possible. You can optimize when what you have works. – Corak Sep 11 '13 at 11:54
  • This question can be linked to http://stackoverflow.com/q/617647 – 131 Feb 11 '15 at 23:11

4 Answers4

18

What about this? I just happen to have this code lying around, it isn't pretty, but it does the job. Just to make sure: One liners are fun, but they usually do not improve readability and code maintainability... So I'd stick to your own solution :)

static string ROT13(string input)
{
    return !string.IsNullOrEmpty(input) ? new string (input.ToCharArray().Select(s =>  { return (char)(( s >= 97 && s <= 122 ) ? ( (s + 13 > 122 ) ? s - 13 : s + 13) : ( s >= 65 && s <= 90 ? (s + 13 > 90 ? s - 13 : s + 13) : s )); }).ToArray() ) : input;            
}

If you need more clarification, just ask.

RvdV79
  • 1,932
  • 15
  • 35
  • 1
    Thanks for your quick reply! I've pasted the code in my program and it works just fine! I think I'll look more into using the ? : operators. – RFerwerda Sep 11 '13 at 10:50
  • That is a conditional statement or just an if ... then ... else ... statement. The if is replaced by the boolean statement (condition) the then is replaced by the ? and the else is replaced by the :. Good luck, glad it worked for you! – RvdV79 Sep 11 '13 at 10:54
6

Just an alternative version that uses other chars in the comparison to make things more "clear"

static string ROT13(string input)
{
  return !string.IsNullOrEmpty(input) ? new string(input.Select(x => (x >= 'a' && x <= 'z') ? (char)((x - 'a' + 13) % 26 + 'a') : ((x >= 'A' && x <= 'Z') ? (char)((x - 'A' + 13) % 26 + 'A') : x)).ToArray()) : input;           
}
Save
  • 10,610
  • 1
  • 16
  • 23
6

Not really a one liner but still shorter than your original code and more understandable than the other answer:

static string Rot13(string input)
{
    if(input == null)
        return null;
    Tuple<int, int>[] ranges = { Tuple.Create(65, 90), Tuple.Create(97, 122) };
    var chars = input.Select(x =>
    {
        var range = ranges.SingleOrDefault(y => x >= y.Item1 && x <= y.Item2);
        if(range == null)
            return x;
        return (char)((x - range.Item1 + 13) % 26) + range.Item1;
    });

    return string.Concat(chars);
}

Another version that even better expresses what happens in ROT13 is this:

static string Rot13(string input)
{
    var lowerCase = Enumerable.Range('a', 26).Select(x => (char)x).ToArray();
    var upperCase = Enumerable.Range('A', 26).Select(x => (char)x).ToArray();
    var mapItems = new[]
    {
        lowerCase.Zip(lowerCase.Skip(13).Concat(lowerCase.Take(13)), (k, v) => Tuple.Create(k, v)),
        upperCase.Zip(upperCase.Skip(13).Concat(upperCase.Take(13)), (k, v) => Tuple.Create(k, v))
    };
    var map = mapItems.SelectMany(x => x).ToDictionary(x => x.Item1, x => x.Item2);

    return new string(input.Select(x => Map(map, x)).ToArray());
}

static char Map(Dictionary<char, char> map, char c)
{
    char result;
    if(!map.TryGetValue(c, out result))
        return c;
    return result;
}
Daniel Hilgarth
  • 166,158
  • 40
  • 312
  • 426
  • 1
    If you only get Int32 representations of each character in the top solution, change `return (char)((x - range.Item1 + 13) % 26) + range.Item1` to `return (char)((x - range.Item1 + 13) % 26 + range.Item1)` – snumpy Aug 30 '17 at 18:29
2

One more an alternative version:

static string ROT13(string input) { String.Join("", input.Select(x => char.IsLetter(x) ? (x >= 65 && x <= 77) || (x >= 97 && x <= 109) ? (char)(x + 13) : (char)(x - 13) : x)) }