Friday, April 17, 2009

Jafar Husain told me that he was working on a challenge by Eric Lippert. David Anson followed and they posted solutions to the challenge on their blog. I thought it was quite a bit of fun so I quickly wrote something up that I like.

I will copy the problem from Erics website. It challenges you to transform a sequence of strings into a nicely delimited form.

(1) If the sequence is empty then the resulting string is "{}".
(2) If the sequence is a single item "ABC" then the resulting string is "{ABC}".
(3) If the sequence is the two item sequence "ABC", "DEF" then the resulting string is "{ABC and DEF}".
(4) If the sequence has more than two items, say, "ABC", "DEF", "G", "H" then the resulting string is "{ABC, DEF, G and H}". (Note: no Oxford comma!)

He noted he was mostly interested in readable code, so I came up with a nice Linq solution. However, Jafar was unimpressed and noted that performance was still important. I was traversing the sequence too often for his taste. Back to the drawing board and coming up with a less readable, but pretty quick implementation:

string lastWord = String.Empty;

StringBuilder sb = new StringBuilder();
foreach(string s in input)
{
    sb.Append(s);
    sb.Append(", ");
    lastWord = s;
}

int lastWordIndex = (sb.Length -1) - lastWord.Length - 3;

if (lastWordIndex > 0)
{
    Console.WriteLine("{" + sb.ToString(0, lastWordIndex) + " and " + lastWord + "}");
}
else if (sb.Length == 0)
{
    Console.WriteLine("{}");
}
else
{
    Console.WriteLine("{" + sb.ToString(0, sb.Length -2) + "}");
}

 

I like this solution quite a bit. Obviously not optimized the return statements, and the readability is quite a bit worse than my first attempt. But it’s short, only goes over the sequence once and should perform well.

edit: The linq version:

StringBuilder t = new StringBuilder();

            int count = input.Count();

            var middleStrings = input.Where((s, i) => i > 0 && i < count - 1);

            t.Append("{");

            t.Append(input.FirstOrDefault());

            // linq reads better than foreach.
            middleStrings.ToList().ForEach(s =>
                                              {
                                                 t.Append(", ");
                                                 t.Append(s);
                                              });

            if (count > 1)
            {
                t.Append(" and ");
                t.Append(input.LastOrDefault());
            }

            t.Append("}");

            return t.ToString();

Oh well.

Saturday, April 18, 2009 9:36:47 AM (Romance Standard Time, UTC+01:00)
How about this simple solution...

string[] elements = new string[] { "ABC", "DEF", "GC", "Z" };

string s = string.Join(", ", elements);
Console.WriteLine(Regex.Replace(s, "^(.*, [^,]*)(?:, )([^,]*)$", "$1 and $2"));
Fred Hirschfeld
Saturday, April 18, 2009 5:05:11 PM (Romance Standard Time, UTC+01:00)
string[] elements1 = new string[] { "ABC" };
string[] elements2 = new string[] { "ABC", "DEF" };
string[] elements3 = new string[] { "ABC", "DEF", "GC", "Z" };

string s = string.Join(", ", elements1);
Console.WriteLine(Regex.Replace(s, "^(.*, )?([^,]*)(?:, )([^,]*)$", "$1$2 and $3"));
s = string.Join(", ", elements2);
Console.WriteLine(Regex.Replace(s, "^(.*, )?([^,]*)(?:, )([^,]*)$", "$1$2 and $3"));
s = string.Join(", ", elements3);
Console.WriteLine(Regex.Replace(s, "^(.*, )?([^,]*)(?:, )([^,]*)$", "$1$2 and $3"));

Just realized that the code did not work in all scenarios... the above would though.
Fred Hirschfeld
Saturday, April 18, 2009 6:13:07 PM (Romance Standard Time, UTC+01:00)
I like it. However, the readability has dropped considerably :)
Ruurd
Friday, August 06, 2010 5:08:18 AM (Romance Standard Time, UTC+01:00)
Hello!everyone!iphone is so expensive.so I like <a rel="nofollow" href='http://www.efox-shop.com'>ciphone</a> <div style="clear: both;">
. d^_^bIt is very interesting.I want to introduce <a href="http://www.efox-shop.com/" rel="nofollow"> efox-shop</a> to you.
<a rel="nofollow" href='http://www.efox-shop.com'>http://www.efox-shop.com</a> <div style="clear: both;">
Monday, August 30, 2010 3:15:39 AM (Romance Standard Time, UTC+01:00)
Let us crazy mbt shoes , <A href="http://www.kissmbtshoes.com">mbt shoes sale</A> of our most popular,you can find the best prices MBT shoes for sell from us .
<A href="http://www.brand-watch-supplier.com/power-balance-c-49/">power balance</A>
<A href="http://www.brand-watch-supplier.com/">brand watch</A>
Go into <A href="http://www.wedding-dresses-mall.com/">wedding dresses</A> look forward to your choice for wedding dresses.
Name
E-mail
Home page

Comment (HTML not allowed)  

Enter the code shown (prevents robots):