Converting a generic list to a CSV string
I have a list of integer values (List) and would like to generate a string of comma delimited values. That is all items in the list output to a single comma delimted list.
My thoughts... 1. pass the list to a method. 2. Use stringbuilder to iterate the list and append commas 3. Test the last character and if it's a comma, delete it.
What are your thoughts? Is this the best way?
How would my code change if I wanted to handle not only integers (my current plan) but strings, longs, doubles, bools, etc, etc. in the future? I guess make it accept a list of any type.
It's amazing what the Framework already does for us.
List<int> myValues;
string csv = String.Join(",", myValues.Select(x => x.ToString()).ToArray());
For the general case:
IEnumerable<T> myList;
string csv = String.Join(",", myList.Select(x => x.ToString()).ToArray());
As you can see, it's effectively no different. Beware that you might need to actually wrap x.ToString()
in quotes (i.e., "\"" + x.ToString() + "\""
) in case x.ToString()
contains commas.
For an interesting read on a slight variant of this: see Comma Quibbling on Eric Lippert's blog.
Note: This was written before .NET 4.0 was officially released. Now we can just say
IEnumerable<T> sequence;
string csv = String.Join(",", sequence);
using the overload String.Join<T>(string, IEnumerable<T>)
. This method will automatically project each element x
to x.ToString()
.
in 3.5, i was still able to do this. Its much more simpler and doesnt need lambda.
String.Join(",", myList.ToArray<string>());
You can create an extension method that you can call on any IEnumerable:
public static string JoinStrings<T>(
this IEnumerable<T> values, string separator)
{
var stringValues = values.Select(item =>
(item == null ? string.Empty : item.ToString()));
return string.Join(separator, stringValues.ToArray());
}
Then you can just call the method on the original list:
string commaSeparated = myList.JoinStrings(", ");
I explain it in-depth in this post. I'll just paste the code here with brief descriptions.
Here's the method that creates the header row. It uses the property names as column names.
private static void CreateHeader<T>(List<T> list, StreamWriter sw)
{
PropertyInfo[] properties = typeof(T).GetProperties();
for (int i = 0; i < properties.Length - 1; i++)
{
sw.Write(properties[i].Name + ",");
}
var lastProp = properties[properties.Length - 1].Name;
sw.Write(lastProp + sw.NewLine);
}
This method creates all the value rows
private static void CreateRows<T>(List<T> list, StreamWriter sw)
{
foreach (var item in list)
{
PropertyInfo[] properties = typeof(T).GetProperties();
for (int i = 0; i < properties.Length - 1; i++)
{
var prop = properties[i];
sw.Write(prop.GetValue(item) + ",");
}
var lastProp = properties[properties.Length - 1];
sw.Write(lastProp.GetValue(item) + sw.NewLine);
}
}
And here's the method that brings them together and creates the actual file.
public static void CreateCSV<T>(List<T> list, string filePath)
{
using (StreamWriter sw = new StreamWriter(filePath))
{
CreateHeader(list, sw);
CreateRows(list, sw);
}
}