Tengo el siguiente conjunto de datos
Donde cada Ciudad pertenece a un Departamento específico, que pertenece a una Región específica, que pertenece a un País específico (en este caso hay un solo país: Francia).
Estos datos están contenidos en un archivo CSV que puedo leer fila por fila, sin embargo, mi objetivo es convertir estos datos en una estructura de árbol (con Francia en la raíz).
A cada uno de estos nodos se le otorgará un valor de Id específico, que es algo que ya he hecho, pero la parte complicada es que cada nodo aquí también debe contener un ParentId (por ejemplo, Belley y Gex necesitan el ParentId de Ain, pero Moulins y Vichy necesitan el ParentId de Aller).
A continuación se muestra un fragmento de código que he escrito que ha asignado un valor de identificación a cada nombre en este conjunto de datos, junto con algunos otros valores:
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); } } La variable levelLists contiene una especie de estructura jerárquica de los datos que busco, pero ninguno de los elementos de esa lista está vinculado entre sí por nada.
¿Alguna idea de cómo podría implementarse esto? Puedo averiguar manualmente cada ParentId, pero me gustaría automatizar este proceso, especialmente si es necesario hacerlo en el futuro.
La solución de @Camilo es realmente buena y pragmática. También sugeriría el uso de un árbol.
Una implementación de muestra:
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; }Una forma de resolver esto es creando diccionarios con los nombres e ID de cada nivel.
Suponiendo que tienes datos como este:
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" }, };Podría hacer algo como esto, que probablemente se pueda mejorar aún más si es necesario:
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); La idea principal es aprovechar el parámetro de index del método Select y la velocidad de los diccionarios para encontrar el ID principal.
Salida de muestra de un violín :
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 }]