【问题标题】:Unable to correctly transform data into hierarchy无法正确地将数据转换为层次结构
【发布时间】:2018-02-16 17:50:07
【问题描述】:

我有两个数据表。 1 包含数据,其他包含行之间的关系信息。代码似乎运行良好,但缺少某些节点。我知道问题的原因,但不确定如何解决问题。

数据表:

DataTable dataTable = new DataTable();
dataTable.Columns.Add("Name");
dataTable.Columns.Add("America");
dataTable.Columns.Add("Japan");
dataTable.Columns.Add("Singapore");

dataTable.Rows.Add("A", 200, 100, 300);
dataTable.Rows.Add("B", 300, 300, 600);
dataTable.Rows.Add("C", 400, 400, 700);
dataTable.Rows.Add("D", 500, 500, 800);
dataTable.Rows.Add("E", 600, 600, 900);
dataTable.Rows.Add("F", 700, 700, 1000);
dataTable.Rows.Add("G", 800, 800, 600);
dataTable.Rows.Add("H", 900, 100, 400);
dataTable.Rows.Add("I", 100, 200, 300);
dataTable.Rows.Add("J", 200, 300, 200);
dataTable.Rows.Add("K", 300, 500, 500);
dataTable.Rows.Add("L", 200, 300, 200);
dataTable.Rows.Add("M", 300, 500, 500);

映射数据表

DataTable mappingTable = new DataTable();
mappingTable.Columns.Add("Name", typeof(string));
mappingTable.Columns.Add("id", typeof(int));
mappingTable.Columns.Add("parentID", typeof(int));

mappingTable.Rows.Add("A", 1, 0);
mappingTable.Rows.Add("B", 2, 1);
mappingTable.Rows.Add("C", 3, 1);
mappingTable.Rows.Add("D", 4, 0);
mappingTable.Rows.Add("E", 5, 4);
mappingTable.Rows.Add("F", 6, 0);
mappingTable.Rows.Add("G", 7, 6);
mappingTable.Rows.Add("H", 8, 6);
mappingTable.Rows.Add("I", 9, 1);
mappingTable.Rows.Add("J", 10, 0);
mappingTable.Rows.Add("K", 11, 10);
mappingTable.Rows.Add("L", 12, 0);
mappingTable.Rows.Add("M", 13, 0);

转换代码

var data = dataTable.Rows.Cast<DataRow>()
    .Select(r => dataTable.Columns.Cast<DataColumn>().ToDictionary(c => c.ColumnName, c => r[c.ColumnName]))
    .ToList();
ILookup<int, int> mapping;
mapping = mappingTable.Rows.Cast<DataRow>()
    .Where(r => !r["parentID"].Equals(0))
    .ToLookup(r => (int)r["parentID"], r => (int)r["id"]);

var output = new List<Dictionary<string, Object>>();
foreach (var group in mapping)
{
    data[@group.Key - 1].Add("children", @group.Select(c => data[c - 1]).ToList());
    output.Add(data[@group.Key - 1]);
}

var json = Newtonsoft.Json.JsonConvert.SerializeObject(output);

缺少节点 (L & M) 父 ID 为 0 的节点,因为该组已被创建。

 {
        "Name": "L",
        "America": "200",
        "Japan": "300",
        "Singapore": "200"

    }, {
        "Name": "M",
        "America": "200",
        "Japan": "300",
        "Singapore": "200"

    }

当前输出:

[{
        "Name": "A",
        "America": "200",
        "Japan": "100",
        "Singapore": "300",
        "children": [{
                "Name": "B",
                "America": "300",
                "Japan": "300",
                "Singapore": "600"
            }, {
                "Name": "C",
                "America": "400",
                "Japan": "400",
                "Singapore": "700"
            }, {
                "Name": "I",
                "America": "100",
                "Japan": "200",
                "Singapore": "300"
            }
        ]
    }, {
        "Name": "D",
        "America": "500",
        "Japan": "500",
        "Singapore": "800",
        "children": [{
                "Name": "E",
                "America": "600",
                "Japan": "600",
                "Singapore": "900"
            }
        ]
    }, {
        "Name": "F",
        "America": "700",
        "Japan": "700",
        "Singapore": "1000",
        "children": [{
                "Name": "G",
                "America": "800",
                "Japan": "800",
                "Singapore": "600"
            }, {
                "Name": "H",
                "America": "900",
                "Japan": "100",
                "Singapore": "400"
            }
        ]
    }
]

预期输出:添加缺失的节点

【问题讨论】:

  • 实际上您没有 L 和 M 的节点,因为它们没有子节点,您添加到输出中的唯一节点是 mapping 中的节点,您可以通过明确排除具有parentID0

标签: c# .net linq c#-4.0 datatable


【解决方案1】:

这是我对所有父母进行循环并添加他们的孩子,然后将他们添加到输出的更改:

var data = dataTable.Rows.Cast<DataRow>()
                    .Select(r => dataTable.Columns.Cast<DataColumn>().ToDictionary(c => c.ColumnName, c => r[c.ColumnName]))
                    .ToList();

var mapping = mappingTable.Rows.Cast<DataRow>()
                          .Where(r => !r["parentID"].Equals(0))
                          .ToLookup(r => (int)r["parentID"], r => (int)r["id"]);

var output = new List<Dictionary<string, Object>>();
foreach (var parent in mappingTable.Rows.Cast<DataRow>().Where(r => r["parentID"].Equals(0))) {
    var parentID = (int)parent["id"];

    if (mapping.Contains(parentID))
        data[parentID-1].Add("children", mapping[parentID].Select(c => data[c-1]).ToList());
    output.Add(data[parentID-1]);
}

【讨论】:

    【解决方案2】:

    就我个人而言,我更喜欢先创建一个模型,这样之后创建树会更容易。

    型号:

        public class DataModel
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int America { get; set; }
            public int Japan { get; set; }
            public int Singapore { get; set; }
            public IList<DataModel> Children { get; set; }
    
            public DataModel()
            {
                Children = new List<DataModel>();
            }
        }
    
        public class DataMapping
        {
            public string Name { get; set; }
            public int Id { get; set; }
            public int ParentId { get; set; }
        }
    

    映射数据:

            var mappings = mappingTable.Rows.Cast<DataRow>()
                .Select(
                    r => new DataMapping
                    {
                        Name = r.Field<string>("Name"),
                        Id = r.Field<int>("id"),
                        ParentId = r.Field<int>("parentID"),
                    }).ToList();
    
            var data = dataTable.Rows.Cast<DataRow>()
                .Select(
                    r => new DataModel
                    {
                        Name = r.Field<string>("Name"),
                        America = Convert.ToInt32(r.Field<string>("America")),
                        Japan = Convert.ToInt32(r.Field<string>("Japan")),
                        Singapore = Convert.ToInt32(r.Field<string>("Singapore")),
                        Id = mappings.Single(m => m.Name.Equals(r.Field<string>("Name"))).Id
                    }).ToList();
    

    创建树:

            foreach (var mapping in mappings.Where(m => m.ParentId != 0))
            {
                var parent = data.Single(d => d.Id == mapping.ParentId);
                var child = data.Single(d => d.Id == mapping.Id);
                parent.Children.Add(child);
            }
    

    输出:

            var json = Newtonsoft.Json.JsonConvert.SerializeObject(data);
    

    【讨论】:

    • 我将通过代码。我使用 DataTable 的原因是因为 First Datatable 中的列可以是 1...n。所以我可能会得到 3 列,我可能会得到 10 列。这就是为什么在这种情况下我无法创建数据模型。
    • 好吧,模型仍然可以保存一个 int 数组,但映射确实可能会变得更加复杂。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-07-15
    • 1970-01-01
    • 1970-01-01
    • 2022-11-10
    • 2022-11-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多