Gage Trader
Gage Trader

Reputation: 363

LINQ to Objects - Generic conversion of ICollection's property to CSV

I've been writing this out a few different places in my code and I want to create a single, generic function, but I can't quite figure out how.

Here is an example of what I'm doing:

public static string FormatCompaniesAsCSV(ICollection<Company> companies)
{
    string csv = "";
    foreach(Company c in companies)
    {
        csv += c.CompanyName + ",";
    }

    return csv.Substring(0, csv.LastIndexOf(","));
}

The problem is that I have to write essentially the same code if I want to display all company SKUs or all of the factories where this is manufactured. The only thing that changes is the object type and the property I am accessing. What I would like to do is something like the following:

public static string FormatCSV<T>(ICollection<T> elements, string propertyName)
{
    string csv = "";
    foreach(T element in elements)
    {
        //I know this next line doesn't work
        //this is just a high level conceptualization of what I want to do
        csv += element.getPropertyValue(propertyName) + ","; 
    }

    return csv.Substring(0, csv.LastIndexOf(","));
}

I need some way to access the properties of the element in the foreach loop, but I don't really know where to start. I've briefly looked at the System.Linq.Expressions library, but I didn't find quite what I was looking for there.

Am I on the right track? If not, is there a better way to accomplish what I would like to do?

Upvotes: 1

Views: 618

Answers (2)

Dvlpr2878
Dvlpr2878

Reputation: 117

The concat with commas can be done in 1 line:

List<string> list = new List<string>() { "Hi", "Old" };
string line = String.Join(",", list.ToArray());

You could also write an extension method (like so)

public static string SuperJoin(string joinCharacter, IEnumerable<string> strings)
{
    return String.Join(",", strings.ToArray());
}

Then use Linq to select out just the field from your object that you want to combine. No delegates needed

Upvotes: 0

Daniel Hilgarth
Daniel Hilgarth

Reputation: 174299

I suggest you pass in a delegate:

public static string FormatAsCSV<T>(IEnumerable<T> elements,
                                    Func<T, string> converter) 
{ 
    string csv = ""; 
    foreach(T element in elements) 
    { 
        csv += converter(element) + ","; 
    } 

    return csv.Substring(0, csv.LastIndexOf(",")); 
} 

You can call this method like this:

FormatAsCsv(companies, x => x.CompanyName);
FormatAsCsv(factories, x => x.ZipCode + " " + x.City);

You could even create an extension method out of it by adding this before the type of the first parameter:

public static string FormatAsCSV<T>(this IEnumerable<T> elements,
                                    Func<T, string> converter) 

Now you can call it like this:

companies.FormatAsCsv(x => x.CompanyName);
factories.FormatAsCsv(x => x.ZipCode + " " + x.City);

BTW: I changed the type from ICollection<T> to IEnumerable<T> as you are only using features of IEnumerable<T>.

And you can improve the generation of the CSV string:

public static string FormatAsCSV<T>(this IEnumerable<T> elements,
                                        Func<T, string> converter) 
{
    return elements.Select(x => converter(x)).Aggregate((x, y) => x + "," + y);
}

Upvotes: 4

Related Questions