【问题标题】:Data Set to Tree Structure数据集为树结构
【发布时间】:2021-08-17 16:05:19
【问题描述】:

我有以下数据集

每个城市属于特定的部门,属于特定的区域,属于特定的国家(在这种情况下只有一个国家:法国)。

此数据包含在一个 CSV 文件中,我可以逐行读取该文件,但我的目标是将这些数据转换为树结构(以法国为根)。

这些节点中的每一个都会被赋予一个特定的 Id 值,这是我已经做过的事情,但棘手的部分是这里的每个节点还必须包含一个 ParentId(例如 Belley 和 Gex 需要 ParentId 的Ain,但 Moulins 和 Vichy 需要 Aller 的 ParentId)。

下面是我编写的一段代码,它为该数据集中的每个名称以及其他一些值分配了一个 Id 值:

int id = 0;
List<CoverageAreaLevel> coverageAreas = GetCoverageAreaDataFromCsv(path, true);
List<LevelList> levelLists = new List<LevelList>
{
    new LevelList { Names = coverageAreas.Select(a => a.Level1).Distinct().ToList(), Level = "1" },
    new LevelList { Names = coverageAreas.Select(a => a.Level2).Distinct().ToList(), Level = "2" },
    new LevelList { Names = coverageAreas.Select(a => a.Level3).Distinct().ToList(), Level = "3" },
    new LevelList { Names = coverageAreas.Select(a => a.Level4).Distinct().ToList(), Level = "4" }
};
List<CoverageArea> newCoverageAreas = new List<CoverageArea>();
foreach (LevelList levelList in levelLists)
{
    foreach (string name in levelList.Names)
    {
        CoverageArea coverageArea = new CoverageArea
        {
            Id = id++.ToString(),
            Description = name,
            FullDescription = name,
            Level = levelList.Level
        };
        newCoverageAreas.Add(coverageArea);
    }
}

levelLists 变量包含我正在寻找的数据的某种层次结构,但该列表中的所有项目都没有通过任何东西链接在一起。

知道如何实现吗?我可以手动找出每个 ParentId,但我想自动化这个过程,特别是如果将来需要这样做。

【问题讨论】:

  • 这听起来像是一个数据库结构问题,而不是 C# 问题。为了阐明您尝试做什么,请编辑您的问题并添加您的代码段,以展示您自己解决此问题的最佳尝试,然后解释您在该代码中的哪个位置没有得到您期望的结果。这将有助于我们更好地了解您的方法,并更加关注您的问题。
  • @devlincarnate 在这里根本不正确,并得出了一些重大结论
  • @JᴀʏMᴇᴇ:要求澄清是草率下结论? :D 而不仅仅是任何结论......大量的结论。 8D

标签: c# .net data-structures tree


【解决方案1】:

@Camilo 的解决方案非常好且实用。我还建议使用树。

一个示例实现:

var countries = models.GroupBy(xco => xco.Country)
        .Select((xco, index) =>
        {
            var country = new Tree<String>();
            country.Value = xco.Key;
            country.Children = xco.GroupBy(xr => xr.Region)
                    .Select((xr, xrIndex) =>
                    {
                        var region = new Tree<String>();
                        region.Value = xr.Key;
                        region.Parent = country;
                        region.Children =
                                xr.GroupBy(xd => xd.Department)
                                    .Select((xd, index) =>
                                    {
                                        var department = new Tree<String>();
                                        department.Value = xd.Key;
                                        department.Parent = region;
                                        department.Children = xd
                                        .Select(xc => new Tree<String> { Value = xc.City, Parent = department });
                                        return department;
                                    });

                        return region;
                    });
            return country;
        });

public class Tree<T>
{
    public IEnumerable<Tree<T>> Children;
    public T Value;
    public Tree<T> Parent;
}

【讨论】:

  • 我的脑海中出现了一棵树的图像,这很有意义,但是很难将思想投入到代码中:P
  • Sonotori(确实是日语)
  • @Delfino 我用示例代码编辑了我之前的答案。
【解决方案2】:

解决此问题的一种方法是使用每个级别的名称和 ID 构建字典。

假设你有这样的数据:

var models = new List<Model> 
{
    new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceA" },
    new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept1", City = "FranceB" },
    new Model { Country = "France", Region = "FranceRegionA", Department = "FranceDept2", City = "FranceC" },
    new Model { Country = "France", Region = "FranceRegionB", Department = "FranceDept3", City = "FranceD" },
    new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept1", City = "ItalyA" },
    new Model { Country = "Italy", Region = "ItalyRegionA", Department = "ItalyDept2", City = "ItalyB" },
};

您可以这样做,如果需要,可能会进一步改进:

var countries = models.GroupBy(x => x.Country)
    .Select((x, index) => Tuple.Create(x.Key, new { Id = index + 1 }))
    .ToDictionary(x => x.Item1, x => x.Item2);

var regions = models.GroupBy(x => x.Region)
    .Select((x, index) => Tuple.Create(x.Key, new { ParentId = countries[x.First().Country].Id, Id = index + 1 }))
    .ToDictionary(x => x.Item1, x => x.Item2);

var departments = models.GroupBy(x => x.Department)
    .Select((x, index) => Tuple.Create(x.Key, new { ParentId = regions[x.First().Region].Id, Id = index + 1 }))
    .ToDictionary(x => x.Item1, x => x.Item2);

var cities = models
    .Select((x, index) => Tuple.Create(x.City, new { ParentId = departments[x.Department].Id, Id = index + 1 }))
    .ToDictionary(x => x.Item1, x => x.Item2);

主要思想是利用Select方法的index参数和字典查找父ID的速度。

样本输出from a fiddle:

countries:
   [France, { Id = 1 }],
   [Italy, { Id = 2 }]

regions:
   [FranceRegionA, { ParentId = 1, Id = 1 }],
   [FranceRegionB, { ParentId = 1, Id = 2 }],
   [ItalyRegionA, { ParentId = 2, Id = 3 }]

departments:
   [FranceDept1, { ParentId = 1, Id = 1 }],
   [FranceDept2, { ParentId = 1, Id = 2 }],
   [FranceDept3, { ParentId = 2, Id = 3 }],
   [ItalyDept1, { ParentId = 3, Id = 4 }],
   [ItalyDept2, { ParentId = 3, Id = 5 }]

cities:
   [FranceA, { ParentId = 1, Id = 1 }],
   [FranceB, { ParentId = 1, Id = 2 }],
   [FranceC, { ParentId = 2, Id = 3 }],
   [FranceD, { ParentId = 3, Id = 4 }],
   [ItalyA, { ParentId = 4, Id = 5 }],
   [ItalyB, { ParentId = 5, Id = 6 }]

【讨论】:

  • 有没有办法将index 分配给一个特定的整数,然后从那里迭代?
  • @Delfino 和上面我的示例中的index + 1 有什么区别? :)
  • 我想没有,但是当谈到 Lambda 表达式时,我的大脑变成了糊状:P
  • 那么,例如,每个城市如何知道其特定的 ParentId,并避免所有城市都被分配相同的 id?
  • @Delfino 通过在ParentId = departments[x.Department].Id 中完成的查找,每个城市都有自己的 parentId(因此在字典中找到具有给定名称的部门并获取其 id)
猜你喜欢
  • 2010-10-30
  • 2010-10-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-05-18
  • 1970-01-01
相关资源
最近更新 更多