tags:

views:

157

answers:

3

Hi,

I have a small algorithm which replaces the position of characters in a String:

class Program
{
    static void Main(string[] args)
    {
        String pairSwitchedStr = pairSwitch("some short sentence");
        Console.WriteLine(pairSwitchedStr);

        Console.ReadKey();
    }

    private static String pairSwitch(String str)
    {
        StringBuilder pairSwitchedStringBuilder = new StringBuilder();
        for (int position = 0; position + 1 < str.Length; position += 2)
        {
            pairSwitchedStringBuilder.Append((char)str[position + 1]);
            pairSwitchedStringBuilder.Append((char)str[position]);
        }
        return pairSwitchedStringBuilder.ToString();
    }
}

I would like to make it as generic as possible, possibly using Generics. What I'd like to have is something which works with:

  • Anything that is built up using a list of instances.
  • Including strings, arrays, linked lists

I suspect that the solution must use generics as the algorithm is working on a list of instances of T (there T is ... something). Version of C# isn't of interest, I guess the solution will be nicer if features from C# version >2.0 is used.

I ended up with: (Where I fixed the error in the above code, it didn't handle odd lengths correctly)

    private static IEnumerable<T> switchSubstitutionCipher<T>(IEnumerable<T> input)
    {
        bool even = false;
        T lastItem = default(T);

        foreach (T element in input)
        {
            if (even) {
                yield return element;
                yield return lastItem;
            }

            even = !even;
            lastItem = element;
        }
        if (even)
            yield return lastItem;
    }
+9  A: 

Strings, in C#, are conceptually very different than an array or linked list. Although a string does implement IEnumerable<Char>, it is an immutable class, so you can't work with it like an IList<T> or Array.

I would recommend leaving your string implementation, then adding a generic implementation for any IList<T>:

private static IList<T> pairSwitch<T>(IList<T> original)
{
    List<T> results = new List<T>(original.Count);

    for (int i=0;i<original.Count-1;i+=2)  // Using -1 to make sure an odd number doesn't throw...
    {
        results.Add(original[i+1]);
        results.Add(original[i]);
    }
    return results;
}
Reed Copsey
+5  A: 
    private static IEnumerable<T> pairSwitch<T>(IEnumerable<T> input)
    {
        T current = default(T);     // just to satisfy compiler
        bool odd = true;
        foreach (T element in input)
        {
            if (odd) current = element;
            else
            {
                yield return element;
                yield return current;
            }
            odd = !odd;
        }
    }

    static void Main(string[] args)
    {
        string test = "Hello";
        string result = new string(pairSwitch(test).ToArray());

Benefits:

  1. Lazy evaluation - which might be important for big lists

  2. String implementation is handled using IEnumerable implementation.

Hightechrider
Wow, two late responses at the same time with virtually the same solution. Even the same boolean name. +1
Adam Robinson
You should probably change the T current = input.FirstOrDefault() to just T current = default(T); No need to enumerate through the input in the event doing so is expensive.
Kleinux
@Kleinux, true, although finding the first element would have been cheap. Shame the complier's static analysis isn't smarter really.
Hightechrider
You replicated my bug too. :-)
Binary255
@Binary255 - how so? In the original current was initialized and it handled odd length input
Hightechrider
It looks to me like it can't handle a single character String, nothing is output as odd will be true. My original algoritm has the same problem. I haven't tested the code as it looked obvious. So you are saying it works?
Binary255
+3  A: 

Alternatively, you could use something like this:

public static class SwitchUtilities
{
    public static IEnumerable<T> PairSwitch<T>(this IEnumerable<T> source)
    {
        T lastItem = default(T);
        bool odd = true;

        foreach(T value in source)
        {
            if(odd)
            {
                lastItem = value;
                odd = false;
            }
            else
            {
                yield return value;
                yield return lastItem;

                odd = true;
            }        
        }
    }
}

This would work to switch pairs of any IEnumerable<T> sequence, which would include string. The only issue here would be that you would get back an IEnumerable<char> rather than a string, but this is easily corrected with:

string value = new String("some short sentence".PairSwitch().ToArray());
Adam Robinson
Compiler will think lastitem is an unassigned variable here (even though it isn't).
Hightechrider
@Hightech: I initialize `lastItem` to `default(T)`.
Adam Robinson
Yeah, that fixes it. But it's still one line longer :-)
Hightechrider
@Hightechrider: True, but yours isn't an extension method ;)
Adam Robinson
You replicated my bug too. :-)
Binary255