Have you ever noticed the goofy logic that must be used to do simple things like creating a comma-delimited string?
public string CommaDelimit(ICollection<string> strings)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (string s in strings)
{
if (first)
first = false;
else
sb.Append(',');
sb.Append(s);
}
return sb.ToString();
}
This method should be hidden away in a static method, and in fact this kind of logic is hidden away in a static method. String.Join() will create a properly delimited string for you. However, it only works with a string array, but as in the example above, oftentimes we’re receiving another type of enumerable.
Luckily, LINQ comes with an extension method to convert IEnumerable<T> to T[]: ToArray().
Let me repeat that since the markup makes it difficult to read. LINQ comes with an extension method to convert a generic IEnumerable of a type to an array of that type: ToArray().
public string CommaDelimit(ICollection<string> strings)
{
return String.Join(",", strings.ToArray());
}
It’s amazing how much ToArray() can simplify a method. In fact, it’s so simple, I would argue that the method to convert the collection to a comma-delimited string is no longer necessary.
Of course, there are more complex situations. How do you handle a collection of non-primitive objects?
public string Names(ICollection<Customer> customers)
{
StringBuilder sb = new StringBuilder();
bool first = true;
foreach (Customer customer in customers)
{
if (first)
first = false;
else
sb.Append(',');
sb.Append(String.Format("{0} {1}",
customer.FirstName, customer.LastName));
}
return sb.ToString();
}
In this example, the method takes in an ICollection of Customer and returns a comma-delimited list of names. A simple ToArray() is not enough. We must retrieve the data from the object and format it prior to making it comma-delimited.
public string Names(ICollection<Customer> customers)
{
var names = from c in customers
select String.Format("{0} {1}",
c.FirstName, c.LastName);
return String.Join(",", names.ToArray());
}
A simple LINQ statement does the trick! Of course, this being C#, there is an alternate way of writing this code.
public string Names(ICollection<Customer> customers)
{
return String.Join(",",
customers.Select(c => String.Format("{0} {1}",
c.FirstName, c.LastName)).ToArray());
}
I prefer the LINQ syntax to the lambda expression in this situation due to improved readability. However, both simplify the statements required to create a comma-delimited string based on data from a list of objects. Keep this in your arsenal for the next time you’re confronted with refactoring goofy logic from the 2.0 days of C#.
Update
I missed another way of doing this for complex objects: Aggregate. Thanks to Richard Mason for calling it to my attention.
public string Names(ICollection<Customer> customers)
{
return customers.Select(c => c.FirstName + " " + c.LastName)
.Aggregate((a, b) => a + "," + b);
}