C#如何实现Dictionary数据拆分行列、对齐算差并导出CSV
C# 实现字典转二维对齐结构方案
核心逻辑设计
覆盖单字典传入、双字典对齐、差值计算全场景,核心处理规则如下:
- 入参兼容1个或2个
Dictionary<string, double>类型数据集,禁止传入空数据集 - 统一提取全量维度:遍历所有传入字典的键,按
-拆分行标识(横杠前半段)、列标识(横杠后半段),对所有行、列标识去重后排序,保证输出结构稳定 - 跨字典对齐规则:对每个(行标识,列标识)的交叉位,若某字典无对应键,该位置填充
double.NaN作为缺失标记,不会出现行列错位 - 双字典场景自动计算差值:仅当两个字典在同一交叉位都有有效数值时,计算
第二个字典值 - 第一个字典值,存在缺失值时差值位同样填充double.NaN - 输出结果为平铺的二维字符串数组,可直接写入CSV,无需二次结构转换
可直接复用的实现代码
using System; using System.Collections.Generic; using System.Linq; using System.Text; public static class DictGridConverter { /// <summary> /// 将1/2个横杠分隔key的字典转换为对齐的二维结构 /// </summary> /// <param name="dicts">待处理字典集合,数量为1或2</param> /// <param name="missingValuePlaceholder">缺失值导出时的占位符,默认为空字符串</param> /// <returns>二维字符串数组,第一行为表头,后续为数据行</returns> /// <exception cref="ArgumentException">传入字典数量不符合要求时抛出</exception> public static string[][] ConvertToGrid(IEnumerable<Dictionary<string, double>> dicts, string missingValuePlaceholder = "") { var dictList = dicts?.ToList() ?? throw new ArgumentNullException(nameof(dicts)); var dictCount = dictList.Count; if (dictCount is <1 or >2) throw new ArgumentException("仅支持传入1个或2个待处理字典"); // 收集全量行、列标识 var allRowKeys = new HashSet<string>(); var allColKeys = new HashSet<string>(); foreach (var dict in dictList) { foreach (var key in dict.Keys) { var keyParts = key.Split('-', 2, StringSplitOptions.RemoveEmptyEntries); if (keyParts.Length != 2) continue; // 过滤格式不符合要求的key allRowKeys.Add(keyParts[0]); allColKeys.Add(keyParts[1]); } } // 排序保证输出顺序稳定 var sortedRows = allRowKeys.OrderBy(r => r).ToList(); var sortedCols = allColKeys.OrderBy(c => c).ToList(); // 生成表头 var header = new List<string> {"行标识"}; if (dictCount == 1) { header.AddRange(sortedCols); } else { foreach (var col in sortedCols) { header.Add($"字典1_{col}"); header.Add($"字典2_{col}"); header.Add($"差值_{col}"); } } var result = new List<string[]> {header.ToArray()}; // 逐行生成数据 foreach (var row in sortedRows) { var rowData = new List<string> {row}; foreach (var col in sortedCols) { var queryKey = $"{row}-{col}"; double val1 = double.NaN; double val2 = double.NaN; if (dictList[0].TryGetValue(queryKey, out var v1)) val1 = v1; if (dictCount == 2 && dictList[1].TryGetValue(queryKey, out var v2)) val2 = v2; rowData.Add(double.IsNaN(val1) ? missingValuePlaceholder : val1.ToString()); if (dictCount == 2) { rowData.Add(double.IsNaN(val2) ? missingValuePlaceholder : val2.ToString()); var diff = double.IsNaN(val1) || double.IsNaN(val2) ? double.NaN : val2 - val1; rowData.Add(double.IsNaN(diff) ? missingValuePlaceholder : diff.ToString()); } } result.Add(rowData.ToArray()); } return result.ToArray(); } /// <summary> /// 辅助方法:将二维数组直接写入CSV文件 /// </summary> public static void WriteToCsv(string[][] gridData, string savePath, string separator = ",") { var sb = new StringBuilder(); foreach (var line in gridData) { var escapedLine = line.Select(cell => { // 处理包含分隔符、引号、换行的内容转义 if (cell.Contains(separator) || cell.Contains('"') || cell.Contains('\n')) return $"\"{cell.Replace("\"", "\"\"")}\""; return cell; }); sb.AppendLine(string.Join(separator, escapedLine)); } File.WriteAllText(savePath, sb.ToString(), Encoding.UTF8); } }
使用示例
// 单字典场景 var dict1 = new Dictionary<string, double> { {"a-b", 1.2}, {"a-c", 3.4}, {"a-d", 5.6}, {"e-b", 7.8} }; var singleDictGrid = DictGridConverter.ConvertToGrid(new[] {dict1}); DictGridConverter.WriteToCsv(singleDictGrid, "单字典输出.csv"); // 双字典场景 var dict2 = new Dictionary<string, double> { {"a-b", 1.5}, {"a-c", 3.0}, {"e-b", 8.2}, {"e-d", 2.1} }; var doubleDictGrid = DictGridConverter.ConvertToGrid(new[] {dict1, dict2}); DictGridConverter.WriteToCsv(doubleDictGrid, "双字典对齐输出.csv");
边界场景处理说明
- 单个字典传入时,自动跳过差值计算逻辑,仅输出基础二维行列结构
- 两个字典的行、列集合不完全重叠时,自动补全所有缺失维度,不会丢失任意字典的条目
- 格式不符合
x-y规范的键会被自动过滤,可根据需要在代码中增加异常key的日志输出 - 数值为0的有效条目不会被误判为缺失值,缺失值默认导出为空字符串,可通过入参自定义占位符
- 自带CSV转义逻辑,单元格内容包含逗号、引号、换行时不会出现CSV格式错乱
内容的提问来源于stack exchange,提问作者Kristielingg
相关产品推荐
相关产品推荐

