How to Comma-Separate a List in One LINQ Statement

by Larry Spencer Sunday, July 1, 2012 12:55 PM

You can use LINQ for a lot more than just conventional querying. Here's some cute code that turns an IEnumerable<T> into a comma-separated string -- using just one LINQ statement.

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace CommaSeparated
{
    class Program
    {
        static void Main(string[] args)
        {
            // Prints "Canada, United States and Mexico"
            Console.WriteLine(CommaSeparate(new[] { "Canada", "United States", "Mexico" }));

            // Prints "1 and 2"
            Console.WriteLine(CommaSeparate(new[] { 1, 2 }));

            // Prints "United States"
            Console.WriteLine(CommaSeparate(new[] { "United States" }));

            // Prints ""
            Console.WriteLine(CommaSeparate(new string[] {  }));

            Console.WriteLine("Press Enter to quit.");
            Console.ReadLine();
        }

        static string CommaSeparate<T>(IEnumerable<T> items)
        {
            return items.Aggregate(
                new StringBuilder(),
                (sb, item) => sb.Append(item).Append(", "),
                sb => Regex.Replace(
                    Regex.Replace(sb.ToString(), ", $", ""), // Remove the final comma and space.
                    ", ([^,]+)$",  // Change the penultimate comma...
                    @" and $1"));  // ...to " and".
        }
    }
}

 

The LINQ Aggregate call in the final method is the key. It takes three parameters:

  1. An initial value for the aggregation process. In our case, that's a StringBuilder.
  2. A Func that takes the aggregate so far and the next item to add, and returns the new aggregate. Here, we append item, a comma and a space to the StringBuilder.
  3. Another Func that lets you come up with the return value based on the final aggregate. In our method, we post-process the final StringBuilder with two regular expressions. The inner one removes the final comma-space. The outer one removes the second-to-last one, if it exists.

There are some limitations.

  • I didn't do anything special if an item already contains a comma.
  • Following typical American usage, I replaced the final comma with the word and. To many people, the Oxford Comma convention makes more sense.
  • I'm sure someone could come up with one Regex that does the work of my two, but I didn't want to get carried away.

I hope you enjoyed seeing how much you can accomplish with the relatively obscure Aggregate method of LINQ.

Tags: ,

All

About the Author

Larry Spencer

Larry Spencer develops software with the Microsoft .NET Framework for ScerIS, a document-management company in Sudbury, MA.