Building delimited lists

As developers we often find ourselves writing the same simple bits of logic over and over again. Today at work I noticed one of my co-workers (let’s call him Benjy) writing a very simple routine to format some output for diagnostics. It went something like this:

        public static string FormatAsCommaDelimitedList(object[] args)
{
var builder = new StringBuilder();
foreach (object arg in args)
{
builder.Append( (builder.Length > 0 ? ", " : "") + arg.ToString());
}
return builder.ToString();
}

I commented that whilst his implementation was fine (and I’m sure we’ve all written something similar in the past) it annoyed me that the condition within the loop made it look so ungainly. It was then that a few things clicked. I mentioned that if it were a simple array of strings we could concatenate using String.Join(). Benjy then applied his philosophy of “thou should never write in a loop what can better be expressed as a query”. And thus was born the following:

        public static string AsCommaDelimitedList<T>(IEnumerable<T> items)
{
return String.Join( ", ", items.Select<T, string>( item => item.ToString() ).ToArray() );
}

Not satisfied with that, Benjy went on to create a neat little CSV routine expressed as an extension method so as to be available to any enumerable collection of objects.

       public static string ToCSV<T>(this IEnumerable<T> items, string delimiter, bool quoteAll)
{
string quoteChar = "\"";
return String.Join(
delimiter,
items.Select<T, string>(
item => item.ToString().Contains(quoteChar) || quoteAll
? String.Format("{0}{1}{0}", quoteChar, item)
: item.ToString()
).ToArray()
);
}

If you ignore his ghastly formatting (and the bug which I’m sure is just there to test me) then that’s pretty cute.

I figured at this point I ought to at least pretend to contribute something – so here’s a version that allows the object to be formatted. Works with IFormattable types like Int32, Decimal, Float, DateTime etc.

        public static string ToFormattedList<T>(this IEnumerable<T> items, string delimiter, string format)
where T : IFormattable
        {
return String.Join(
delimiter,
items.Select<T, string>(
item => item.ToString(format, System.Globalization.CultureInfo.CurrentCulture)
).ToArray());
}

In deference to Benjy (a strong believer in console applications and mono-spaced fonts) here’s the simple Console Main() I used to test these:

        public static void Main(string[] args)
{
Console.WriteLine(args.ToCSV(", ", true));
Console.WriteLine(args.AsCommaDelimitedList());
var amounts = new decimal[] { 2.1m, 4, 4.5m, 9, 3.333m, 4.14m };
Console.WriteLine(amounts.ToCSV(", ", true));
Console.WriteLine(amounts.AsCommaDelimitedList());
Console.WriteLine(amounts.ToFormattedList("  |  ", "$#,##0.00"));
Console.ReadLine();
}

And the corresponding output in all its dumb terminal glory.

"The", "quick", "brown", "fox", "jumped", "over", "the", "lazy", "dog."

The, quick, brown, fox, jumped, over, the, lazy, dog.

"2.1", "4", "4.5", "9", "3.333", "4.14"

2.1, 4, 4.5, 9, 3.333, 4.14

$2.10  |  $4.00  |  $4.50  |  $9.00  |  $3.33  |  $4.14

On the subject of formatting I enjoyed this blog post on Visual Studio Debugging Tips. Particularly the explanation of how to use conditional formatting with the DebuggerDisplay attribute. Worth a look!

2 thoughts on “Building delimited lists”

  1. Thanks Nigel.. You’ve spared me the effort.

    Ok.. I’ll bite – what’s wrong with the formatting, the mono-spaced font, and console apps??? 🙂 Let’s here it?

Comments are closed.