简体   繁体   中英

How to convert Dictionary <string,List<string>> in C# to csv with keys as headers?

I have a Dictionary like:

{
   key1 : List1 {value1a, value2a},
   key2 : List2 {value1b, value2b},
   ...
}

How can I get this into a csv that looks like:

key1,key2
value1a,value1b
value2a,value2b
...

I've tried first converting this to a DataTable but am having trouble with my Linq fundamentals.

dataTable.Columns.AddRange(dataDictionary
  .Keys
  .Select(x => new DataColumn(x, dataDictionary[x])).ToArray());
dataDictionary[x] List<string> can't be converted to System.Type.

I've also tried the direct-to-csv route but again I am having trouble remembering how to access the values of the list in LINQ correctly and can't get the keys as headers and the lists into columns:

File.WriteAllLines($@"{args[3]}\{args[0]}_{args[1]}_{args[2]}.csv",
  dataDictionary.Select(x => x.Key + ";" + x.Value + ";"));

If you are looking for csv (say, csv file), not DataTable you can implement csv lines generator straightforward:

Code:

using System.Linq;

...

private static IEnumerable<string> ToCsv(Dictionary<string, List<String>> data) {
  // Quotation if required: 123,45 -> "123,45"; a"bc -> "a""bc" 
  string Quote(string value) => string.IsNullOrEmpty(value) 
      ? "" : value.Contains(',') || value.Contains('"') 
      ? "\"" + value.Replace("\"", "\"\'") + "\""
        : value;

  int rowCount = data.Max(pair => pair.Value.Count);

  // Captions
  yield return string.Join(",", data.Select(pair => Quote(pair.Key)));

  // Rows one after one 
  for (int r = 0; r < rowCount; ++r) 
    yield return string.Join(",", data
      .Select(pair => Quote(r < pair.Value.Count ? pair.Value[r] : "")));
}  

Demo:

  Dictionary<string, List<string>> data = new Dictionary<string, List<string>>() {
    { "column1", new List<string>() { "C1_A", "C1_B"} },
    { "column2", new List<string>() { "C2_A", "C2_B", "C2_C"} },
    { "column3", new List<string>() { } },
    { "column4", new List<string>() { "C4_A" } },
  };

  // Generate all lines of the csv and combine them with \r\n:
  string csvText = string.Join(Environment.NewLine, ToCsv(data));

  Console.Write(csvText);

Outcome:

column1,column2,column3,column4
C1_A,C2_A,,C4_A
C1_B,C2_B,,
,C2_C,,

In your case (writing csv into a file ) you can put it as

File.WriteAllLines(@"c:\demo.csv", ToCsv(myDictionary));

Firstly, you are getting below error

dataDictionary[x] List<string> can't be converted to System.Type.

because, DataColumn have multiple overloaded constructor and you are trying call below, which is unable to cast List<string> to Type .

public DataColumn(string columnName, Type dataType)

Second, below is the code with assuming that every List<string> have same length.

using System.IO;
using System.Linq;
using System.Text;
using System;
using System.Collections.Generic;
using System.Data;

class Program
{
    static void Main(string[] args)
    {
        Dictionary<string, List<string>> values = new Dictionary<string, List<string>>();

        values.Add("key1", new List<string>() { "value1a", "value2a" });
        values.Add("key2", new List<string>() { "value1b", "value2b" });

        DataTable dataTable = new DataTable();

        dataTable.Columns.AddRange(values.Keys.Select(x => new DataColumn(x)).ToArray());

        var totalColumns = dataTable.Columns.Count;

        for(int i = 0; i < totalColumns; i++)
        {
            DataRow dataRow = dataTable.NewRow();

            int fieldNumber = 0;
            foreach(var item in values)
            {
                dataRow[fieldNumber] = values[item.Key][i];
                fieldNumber++;
            }

            dataTable.Rows.Add(dataRow);
        }

        dataTable.WriteToCsvFile("D://Test.csv");
    }
}

public static class Extension
{
    public static void WriteToCsvFile(this DataTable dataTable, string filePath)
    {
        var content = dataTable.GenerateCsvBytes();

        File.WriteAllBytes(filePath, content);
    }

    public static byte[] GenerateCsvBytes(this DataTable dataTable)
    {
        StringBuilder fileContent = new StringBuilder();

        foreach (var col in dataTable.Columns)
        {
            fileContent.Append(col.ToString() + ",");
        }

        fileContent.Replace(",", Environment.NewLine, fileContent.Length - 1, 1);

        foreach (DataRow dr in dataTable.Rows)
        {
            foreach (var column in dr.ItemArray)
            {
                fileContent.Append("\"" + column.ToString() + "\",");
            }

            fileContent.Replace(",", Environment.NewLine, fileContent.Length - 1, 1);
        }

        return Encoding.ASCII.GetBytes(fileContent.ToString());
    }
}

Result:

key1,key2
value1a,value1b
value2a,value2b

This will fail if data.Keys.Count != data.Values[0].Count .

var keys = data.Keys;
Console.WriteLine(String.Join(",", keys));
for (int j1 = 0; j1 < keys.Count; ++j1)
    Console.WriteLine(String.Join(",", data.Values.Select(v => v[j1])));

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM