如何将 C# 中的 Dictionary <string,List<string>> 转换为带有键作为标题的 csv?

Posted

技术标签:

【中文标题】如何将 C# 中的 Dictionary <string,List<string>> 转换为带有键作为标题的 csv?【英文标题】:How to convert Dictionary <string,List<string>> in C# to csv with keys as headers? 【发布时间】:2021-05-06 23:29:29 【问题描述】:

我有一个像这样的字典:


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

我怎样才能把它变成一个 csv 看起来像:

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

我已尝试先将其转换为 DataTable,但在我的 Linq 基础知识方面遇到了问题。

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

我也尝试过 direct-to-csv 路由,但我再次无法记住如何正确访问 LINQ 中列表的值,并且无法将键作为标题并将列表放入列:

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

【问题讨论】:

如果您选择将字段放入类或结构中,您可能会发现CSVHelper 很有用。但是,它似乎是针对具有硬编码列的。 【参考方案1】:

如果您正在寻找 csv(例如 csv 文件),而不是 DataTable,您可以直接实现 csv 行生成器:

代码:

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] : "")));
  

演示:

  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);

结果:

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

在你的情况下(将 csv 写入 文件)你可以把它写成

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

【讨论】:

太棒了,我以前什至没见过'yield'。感谢分享。【参考方案2】:

首先,你遇到了错误

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

因为,DataColumn 有多个重载的构造函数,而您尝试在下面调用,它无法将 List&lt;string&gt; 转换为 Type

public DataColumn(string columnName, Type dataType)

其次,下面是假设每个List&lt;string&gt; 具有相同长度的代码。

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());
    

结果:

key1,key2
value1a,value1b
value2a,value2b

【讨论】:

嘿 Nayan,这真的很棒。我认为这只是添加与列一样多的行,但这仅适用于这个 2x2 示例。你可以修改 for 循环以根据列表长度填充行吗? @MarkMcGown 如果键的数量与列表长度不同,您如何获得 CSV? @NetMage 列表长度将成为该列表的键是标题的列的行数。 @MarkMcGown 很抱歉,我觉得这不太好理解。也许在您的问题中添加更多示例。在 CSV 文件中,所有列的长度都相同 - 在 CSV 文件中,# 列等于键的数量。【参考方案3】:

如果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])));

【讨论】:

以上是关于如何将 C# 中的 Dictionary <string,List<string>> 转换为带有键作为标题的 csv?的主要内容,如果未能解决你的问题,请参考以下文章

如何读取C# object的值

C# Dictionary<> 和可变键

关于c#中Dictionary的使用

如何在 C# 中将 Dictionary<string, object> 转换为 Dictionary<string, string>

C# 如何通过Key键获取Dictionary集合内的Value

将两个列表映射到 C# 中的字典中