Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Writing a list of complex objects to CSV file using CSVHelper

I have a list of class objects, which in turn contain a list of another class objects. They look like this:

public class Column
{
    public string ColName { get; set; }
    public List<Item> ItemList { get; set; }
}

public class Item
{
    public DateTime TimeStamp { get; set; }
    public double Value { get; set; }
}

They have two important properties:

  1. The Length of List<Item> ItemList in all columns will be the same. I won't know what that length is until run-time though.
  2. The actual values of TimeStamp in each column will be the same. In other words, the TimeStamp list in each column will be identical.

If you look at the following mock function that I wrote to create a list of Column objects you'll get a clear picture. This is an accurate description of what my actual data (which I get from somewhere else in the program) will look like:

private static List<Column> GetColumns()
{
    var dt1 = DateTime.Now;
    var dt2 = dt1.AddSeconds(1);
    var dt3 = dt2.AddSeconds(1);
    var dt4 = dt3.AddSeconds(1);

    var col1 = new Column()
    {
        ColName = "ABC",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 1 },
            new Item() { TimeStamp = dt2, Value = 2 },
            new Item() { TimeStamp = dt3, Value = 3 },
            new Item() { TimeStamp = dt4, Value = 4 }
        }
    };

    var col2 = new Column()
    {
        ColName = "XYZ",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 4 },
            new Item() { TimeStamp = dt2, Value = 3 },
            new Item() { TimeStamp = dt3, Value = 2 },
            new Item() { TimeStamp = dt4, Value = 1 }
        }
    };

    var col3 = new Column()
    {
        ColName = "KLM",
        ItemList = new List<Item>
        {
            new Item() { TimeStamp = dt1, Value = 1 },
            new Item() { TimeStamp = dt2, Value = 2 },
            new Item() { TimeStamp = dt3, Value = 4 },
            new Item() { TimeStamp = dt4, Value = 8 }
        }
    };

    var list = new List<Column>
    {
        col1,
        col2,
        col3,
    };

    return list;
}

An important note is that I will not know the length of List<Column> until run-time, nor will I know the length of ItemList inside them until run-time. Furthermore, I will not know the names of each column until run-time. Now my goal is to write this information into a CSV file, of the following format.

enter image description here

I feel like dynamic is the way to go here, so I started with this:

var columns = GetColumns();

var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();

var writeList = new List<dynamic>();
for (int i = 0; i < timeStamps.Count; i++)
{
    dynamic csvItem = new ExpandoObject();
    csvItem.TimeStamp = timeStamps[i];
    // How to get columns?
    writeList.Add(csvItem);
}

using (var writer = new StreamWriter("output.csv"))
{
    using (var csv = new CsvHelper.CsvWriter(writer))
    {
        csv.WriteRecords(writeList);
    }
}

This is a start, but since I don't know the number of columns I will have and the names of them, I'm not sure how to proceed from here. Is using dynamic not an option here?

Alternatively I could forgo using CSVHelper at all, and write something from scratch like the following, but it's a bit messy. I'm iterating through the column list twice, and three loops in total. I'm looking for a more elegant solution, if possible.

var columns = GetColumns();

var timeStamps = columns.First().ItemList.Select(x => x.TimeStamp).ToList();

var headers = "TimeStamp";
foreach (var col in columns)
{
    headers += "," + col.ColName;
}

using (var fs = new FileStream("output.csv", FileMode.Create, FileAccess.Write))
{
    using (var sw = new StreamWriter(fs))
    {
        sw.WriteLine(headers);
        for (int i = 0; i < timeStamps.Count; i++)
        {
            var line = timeStamps[i].ToString("yyyy/MM/dd HH:mm:ss");
            foreach (var col in columns)
            {
                line += "," + col.ItemList[i].Value;
            }
            sw.WriteLine(line);
        }
    }
}
like image 883
Sach Avatar asked Oct 17 '25 09:10

Sach


1 Answers

This is probably the best you can do with CsvHelper for your requirements.

using (var writer = new StreamWriter("output.csv"))
{
    using (var csv = new CsvHelper.CsvWriter(writer))
    {
        var columns = GetColumns();

        // Write header
        csv.WriteField("TimeStamp");

        foreach (var column in columns)
        {
            csv.WriteField(column.ColName);
        }

        csv.NextRecord();

        // Write rows
        for (int i = 0; i < columns[0].ItemList.Count; i++)
        {
            csv.WriteField(columns[0].ItemList[i].TimeStamp.ToString("yyyy/MM/dd HH:mm:ss"));

            foreach (var column in columns)
            {
                csv.WriteField(column.ItemList[i].Value);
            }

            csv.NextRecord();
        }
    }
}
like image 182
David Specht Avatar answered Oct 19 '25 22:10

David Specht



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!