Comma Quibbling

Comma Quibbling

Rate This

[UPDATE: Holy goodness. Apparently this was a more popular pasttime than I anticipated. There's like a hundred solutions in there. Who knew there were that many ways to stick commas in a string? It will take me some time to go through them all, so don't be surprised if it's a couple of weeks until I get them all sorted out.]

Comma The point of Monday’s post about comma-separated lists was not so much about the actual problem; it’s a rather trivial problem. Rather, I wanted to make two points. First, stating the actual problem rather than a much harder and more general version of the problem is likely to get you a realistic solution to your actual problem much faster. And second, reworking the statement of the problem into an equivalent but structurally different statement is a great way to see solutions that you might have otherwise missed.

But whenever I make a post illustrating such points with a specific example, lots of people pipe up with their ideas for how to solve the specific example. Which is awesome; I encourage this behaviour.

So in that spirit, here’s a slightly harder version of the string concatenation problem, just for the fun of it. Write me a function that takes a non-null IEnumerable<string> and returns a string with the following characteristics:

(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!)

I think you get the idea. You can post your solution in the comments or use the link on the blog page to email your solution to me.

The strings in the sequence can be assumed to be non-null but can otherwise be any string value, including empty strings or strings containing commas, braces and "and".

There’s no size limit on the sequence; it could be tiny, it could be thousands of strings. But it will be finite.

All you get are the methods of IEnumerable<string>; if you want to make that thing into a list or an array, you’re going to need to do that explicitly rather than casting it and hoping for the best.

I am particularly interested in solutions which make the semantics of the code very clear to the code maintainer.

Of course, C# is most interesting to me, but if there are neat ways to express this in other languages, I’d love to see them too.

If there are any particularly amusing or interesting implementations I’ll dissect them on the blog in a future episode, probably in a week or so. I’m not going to have time to do a detailed analysis of every one.

And… go!

  • @M. McCulloch -They would never work for me either. Your sample contains identfiers that are longe than necessary, redundant white space (and line breaks). All of these items take longer to type, and longer to Parse. There is absolutely NO POINT in wasting these resources!!!!!

    FWIW: There was a time when those arguments actually held some weight....glad they are ancient history!

  • My $0.02 (one-liner)

    static string Do(params string[] input)

    {

    return String.Concat(

    "{",

    input.Length > 2 ?

    String.Concat(

    String.Join(", ", input.Take(input.Length - 1)),

    " and ",

    input.Last()) :

    String.Join(" and ", input),

    "}");

    }

    static void Main(string[] args)

    {

    Console.WriteLine(Do("")); // {}

    Console.WriteLine(Do("ABC")); // {ABC}

    Console.WriteLine(Do("ABC", "DEF")); // {ABC and DEF}

    Console.WriteLine(Do("ABC", "DEF", "G", "H")); // {ABC, DEF, G and H}

    }

  • @David V. Corbin: Whut?? Understand NOTHING from your message.

  •    public class StringSequenceFormatter

       {

           public string Format(IEnumerable<string> sequence)

           {

               string commaList = RecursiveFormatter(sequence);

               return '{' + commaList + '}';

           }

           private string RecursiveFormatter(IEnumerable<string> sequence)

           {

               if (sequence.Count() == 0)

               {

                   return string.Empty;

               }

               else if (sequence.Count() == 1)

               {

                   return sequence.First();

               }

               else if (sequence.Count() == 2)

               {

                   return sequence.First() + " and " + sequence.Last();

               }

               else

               {

                   return sequence.First() + ", " + RecursiveFormatter(sequence.Skip(1));

               }

           }

       }

  • Some of these solutions are so... odd that it makes me scratch my head wondering why.

    Here's my take, straight forward:

    public static string EnglishWordConcatination(this IEnumerable<string> source)

    {

       /* *

           * If the elements passed in are already an array,

           * use it.

           * */

       string[] sourceArray = source as string[];

       int itemCount = 0;

       if (sourceArray == null)

       {

           /* *

               * If the elements passed in are already a type-strict

               * collection of strings, use it.

               * */

           var sourceCollection = source as ICollection<string>;

           if (sourceCollection != null)

           {

               sourceArray = new string[itemCount = sourceCollection.Count];

               sourceCollection.CopyTo(sourceArray, 0);

           }

           else

           {

               /* *

                   * Otherwise, build a new array assuming

                   * a small working base set of elements.

                   * */

               sourceArray = new string[2];

               foreach (var element in source)

               {

                   if (itemCount == sourceArray.Length)

                   {

                       string[] tempArray = new string[itemCount * 2];

                       sourceArray.CopyTo(tempArray, 0);

                       sourceArray = tempArray;

                   }

                   sourceArray[itemCount++] = element;

               }

           }

       }

       else

           itemCount = sourceArray.Length;

       StringBuilder sb = new StringBuilder();

       sb.Append('{');

       bool first = true;

       for (int i = 0; i < itemCount; i++)

       {

           /* *

               * The condition is exclusive, the last is Length - 1.

               * */

           bool last = (i == (itemCount - 1));

           /* *

               * Special conditions are the first and last since

               * the separator is appended prior to the current

               * element.

               * */

           if (first)

               first = false;

           else if (last)

               sb.Append(" and ");

           else

               sb.Append(", ");

           sb.Append(sourceArray[i]);

       }

       sb.Append('}');

       return sb.ToString();

    }

  • Just realized this was an old post, but a puzzle is a puzzle.

    This is what I came up with in C#.

    public string CommaQuibble(IEnumerable<string> data)

           {

               StringBuilder result = new StringBuilder();

               int stringCount = data.Count();

               int currentStringIndex = 1;

               foreach(string str in data)

               {

                   if(currentStringIndex == 1)

                       result.Append(str);

                   else

                   if(currentStringIndex < stringCount )

                       result.Append("," + str);

                   else

                   if(currentStringIndex == stringCount)

                       result.Append(" and " + str);

                   currentStringIndex++;

               }

               return string.Format("{{{0}}}", result.ToString());

           }

  • Without too many or nested if statements. Flat code.

           public static string CommaQuibbling(IEnumerable<string> items)

           {

               var result = new StringBuilder("{");

               string separator, last;

               last = separator = "";

               bool second = false;

               var iter = items.GetEnumerator();

               if (iter.MoveNext())

                   result.Append(iter.Current);

               while (iter.MoveNext())

               {

                   second = true;

                   result.Append(separator).Append(last);

                   last = iter.Current;

                   separator = ",";

               }

               result.Append(second ? " and ": separator).Append(last);

               return result.Append("}").ToString();

           }

Page 19 of 19 (277 items) «1516171819