Photo by Mika Baumeister

When working with C# and CSV files, I always use CSVHelper. It's a small library for reading and writing CSV files. Extremely fast, flexible, and easy to use. I couldn't agree more, it's simply awesome :).

Now, I ran into a rather delicate problem a couple of days ago.
Say that you a CSV file looking like this:

id,categories
1234,"food,pasta,meat"
4321,"food,beer,alcohol"

You then have a class that you want to map the data to:

public class GoodFoodCsvRow
{
    public string Id { get; set; }
    public List<string> Categories { get; set; }
}

And your CSVHelper Map looks like this:

public class GoodFoodCsvRowMap : ClassMap<GoodFoodCsvRow>
{
    public GoodFoodCsvRowMap()
    {
        Map(x => x.Id).Index(0);
        Map(x => x.Categories).Index(1);
    }
}

If you now were to read said CSV file like this...

using (var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead))
{
    response.EnsureSuccessStatusCode();
    using (var responseStream = await response.Content.ReadAsStreamAsync())
    {
        using (var reader = new StreamReader(responseStream))
        {
            using (var csv = new CsvReader(reader, CultureInfo.InvariantCulture))
            {
                csv.Configuration.HasHeaderRecord = true;
                csv.Configuration.RegisterClassMap<GoodFoodCsvRowMap>();
                var rows = csv.GetRecords<GoodFoodCsvRow>();
                return rows.ToDictionary(x => x.Id, x => x.Categories);
            }
        }
    }
}

...you would notice that the Categories list only contains one string looking like this:
dictionary-before

That's not what we want, we want each category to be a separate item in the Categories list.

Luckily, it's really easy to achieve using CSVHelper, we just need to update our GoodFoodCsvRowMap so it looks like this:

public class GoodFoodCsvRowMap : ClassMap<GoodFoodCsvRow>
{
    public GoodFoodCsvRowMap()
    {
        Map(x => x.Id).Index(0);
        Map(x => x.Categories).Index(1).ConvertUsing(row =>
        {
            var columnValue = row.GetField<string>("categories");
            return columnValue?.Split(',')?.ToList() ?? new List<string>();
        });
    }
}

Now when we run it again the problem will be solved:
csvhelper-list-after.