字典到DataTable

我有一个非常奇怪的问题,并且不知道我应该采取哪种方式来修复它。

我有一个IEnumerable<Dictionary> ,它可以包含一个或多个IEnumerable<Dictionary>

现在,需要将此Dictionary导入到DataTable中,如果IEnumberable<Dictionary> inside里面有0 IEnumberable<Dictionary> ,那么DataTable应该只包含一行,列名为字符串,RowData为对象(字符串)在这种情况下)。 但是,如果有子节点,则DataTable应包含与此子节点相同的行数,以及父节点的每一行中的其他信息。

例如,父词典具有以下值:

字符串,对象
 ---------------
姓名,迈克
 LastName,泰森

IEnumerable Dictionary子项有:

字符串,对象
 ----------------
 [0]
 ChildName,约翰
 ChildAge,10岁

 [1]
 ChildName,Tony
 ChildAge,12岁

结果应该是:

名称LastName ChildName ChildAge
 --------------------------------------------
 Mike Tyson John 10
迈克泰森托尼12

另外,Parent IEnumerable可以有许多子IEnumerable,但它们都具有相同的大小。

有谁知道如何解决这个问题?

 static void Main(string[] args) { var child1 = new List<Dictionary>(); var childOneDic = new Dictionary { { "ChildName", "John" }, { "ChildAge", 10 } }; child1.Add(childOneDic); var child2 = new List<Dictionary>(); var childTwoDic = new Dictionary { { "ChildName", "Tony" }, { "ChildAge", 12 } }; child2.Add(childTwoDic); var parrent = new List<Dictionary>(); var parrentDic = new Dictionary { { "Name", "Mike" }, { "LastName", "Tyson" }, { "child1", child1 }, { "child2", child2 } }; parrent.Add(parrentDic); var table = new DataTable(); table.Columns.Add("Name"); table.Columns.Add("LastName"); table.Columns.Add("ChildName"); table.Columns.Add("ChildAge"); table = CreateTable(parrent, null, table); } static DataTable CreateTable(IEnumerable<Dictionary> parrent, DataRow row, DataTable table) { if (row == null) { row = table.NewRow(); } foreach (var v in parrent) { foreach (var o in v) { if (o.Value.GetType().IsGenericType) { var dic = (IEnumerable<Dictionary>) o.Value; CreateTable(dic, row, table); } else { row[o.Key] = o.Value; } } if (row.RowState == DataRowState.Added) { DataRow tempRow = table.NewRow(); tempRow.ItemArray = row.ItemArray; table.Rows.Add(tempRow); } else { table.Rows.Add(row); } } return table; } 

Linq是这份工作的好人选。 我仍然认为你应该重新考虑设计,这是一件非常可怕的事情。 这应该做(并且没有任何硬编码):

 var child1 = new List> { new Dictionary { { "ChildName", "John" }, { "ChildAge", 10 } } }; var child2 = new List> { new Dictionary { { "ChildName", "Tony" }, { "ChildAge", 12 } } }; var parent = new List> { new Dictionary { { "Name", "Mike" }, { "LastName", "Tyson" }, { "child1", child1 }, { "child2", child2 } }, new Dictionary { { "Name", "Lykke" }, { "LastName", "Li" }, { "child1", child1 }, }, new Dictionary { { "Name", "Mike" }, { "LastName", "Oldfield" } } }; CreateTable(parent); static DataTable CreateTable(IEnumerable> parents) { var table = new DataTable(); foreach (var parent in parents) { var children = parent.Values .OfType>>() .ToArray(); var length = children.Any() ? children.Length : 1; var parentEntries = parent.Where(x => x.Value is string) .Repeat(length) .ToLookup(x => x.Key, x => x.Value); var childEntries = children.SelectMany(x => x.First()) .ToLookup(x => x.Key, x => x.Value); var allEntries = parentEntries.Concat(childEntries) .ToDictionary(x => x.Key, x => x.ToArray()); var headers = allEntries.Select(x => x.Key) .Except(table.Columns .Cast() .Select(x => x.ColumnName)) .Select(x => new DataColumn(x)) .ToArray(); table.Columns.AddRange(headers); var addedRows = new int[length]; for (int i = 0; i < length; i++) addedRows[i] = table.Rows.IndexOf(table.Rows.Add()); foreach (DataColumn col in table.Columns) { object[] columnRows; if (!allEntries.TryGetValue(col.ColumnName, out columnRows)) continue; for (int i = 0; i < addedRows.Length; i++) table.Rows[addedRows[i]][col] = columnRows[i]; } } return table; } 

这是我用过的一种扩展方法:

 public static IEnumerable Repeat(this IEnumerable source, int times) { source = source.ToArray(); return Enumerable.Range(0, times).SelectMany(_ => source); } 

你可以用更惯用的方式创建addedRows变量(我更喜欢),但对其他人来说addedRows 。 在一行中,像这样:

 var addedRows = Enumerable.Range(0, length) .Select(x => new { relativeIndex = x, actualIndex = table.Rows.IndexOf(table.Rows.Add()) }) .ToArray(); 

这里棘手的部分是让旋转正确。 在我们的案例中没什么大不了的,因为我们可以利用索引器 用一组例子进行测试,让我知道这是否有问题。


另一种方法是预先计算标题(循环之前的数据表列),因为它无论如何都不会改变。 但这也意味着一轮额外的枚举。 至于哪个更高效,你将不得不测试它..我发现第一个更优雅。

 static DataTable CreateTable(IEnumerable> parents) { var table = new DataTable(); //excuse the meaningless variable names var c = parents.FirstOrDefault(x => x.Values .OfType>>() .Any()); var p = c ?? parents.FirstOrDefault(); if (p == null) return table; var headers = p.Where(x => x.Value is string) .Select(x => x.Key) .Concat(c == null ? Enumerable.Empty() : c.Values .OfType>>() .First() .SelectMany(x => x.Keys)) .Select(x => new DataColumn(x)) .ToArray(); table.Columns.AddRange(headers); foreach (var parent in parents) { var children = parent.Values .OfType>>() .ToArray(); var length = children.Any() ? children.Length : 1; var parentEntries = parent.Where(x => x.Value is string) .Repeat(length) .ToLookup(x => x.Key, x => x.Value); var childEntries = children.SelectMany(x => x.First()) .ToLookup(x => x.Key, x => x.Value); var allEntries = parentEntries.Concat(childEntries) .ToDictionary(x => x.Key, x => x.ToArray()); var addedRows = Enumerable.Range(0, length) .Select(x => new { relativeIndex = x, actualIndex = table.Rows.IndexOf(table.Rows.Add()) }) .ToArray(); foreach (DataColumn col in table.Columns) { object[] columnRows; if (!allEntries.TryGetValue(col.ColumnName, out columnRows)) continue; foreach (var row in addedRows) table.Rows[row.actualIndex][col] = columnRows[row.relativeIndex]; } } return table; } 

我建议你使用类,这样可以更容易地查看和操作数据。 以下是基于您给出的示例可以执行的操作的示例。 诀窍还在于保留孩子内部父母的引用。 你只需要将子列表传递给网格。

  static void Main() { var child1 = new List>(); var childOneDic = new Dictionary { { "ChildName", "John" }, { "ChildAge", 10 } }; child1.Add(childOneDic); var child2 = new List>(); var childTwoDic = new Dictionary { { "ChildName", "Tony" }, { "ChildAge", 12 } }; child2.Add(childTwoDic); var parrent = new List>(); var parrentDic = new Dictionary { { "Name", "Mike" }, { "LastName", "Tyson" }, { "child1", child1 }, { "child2", child2 } }; parrent.Add(parrentDic); List goodList = new List(); List allChilds = new List(); foreach (Dictionary p in parrent) { Parent newParent = new Parent(p); goodList.Add(newParent); allChilds.AddRange(newParent.Childs); } foreach (Child c in allChilds) { Console.WriteLine(c.ParentName + ":" + c.ParentName + ":" + c.Name + ":" + c.Age); } Console.ReadLine(); } public class Parent { private List _childs = new List(); private Dictionary _dto; public Parent(Dictionary dto) { _dto = dto; for (int i = 0; i <= 99; i++) { if (_dto.ContainsKey("child" + i)) { _childs.Add(new Child(((List>)_dto["child" + i])[0], this)); } } } public string Name { get { return (string)_dto["Name"]; } } public string LastName { get { return (string)_dto["LastName"]; } } public List Childs { get { return _childs; } } } public class Child { private Parent _parent; private Dictionary _dto; public Child(Dictionary dto, Parent parent) { _parent = parent; _dto = dto; } public string Name { get { return (string)_dto["ChildName"]; } } public int Age { get { return (int)_dto["ChildAge"]; } } public string ParentName { get { return _parent.Name; } } public string ParentLastName { get { return _parent.LastName; } } } }