数据集为树结构

Data Set to Tree Structure

我有下面这组数据

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

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

这些节点中的每一个都将被赋予一个特定的 Id 值,这是我已经完成的事情,但棘手的部分是这里的每个节点还必须包含一个 ParentId(例如 Belley 和 Gex 需要Ain 的 ParentId,但 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 变量包含我要查找的数据的一种层次结构,但该列表中的 none 项通过任何东西链接在一起。

知道如何实施吗?我可以手动找出每个 ParentId,但我想自动执行此过程,尤其是在将来需要这样做的情况下。

解决此问题的一种方法是使用每个级别的名称和 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 }]

@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;
}