我有一个平面的类别列表,如以下类所示
public class FlatCategoryList
{
public List<FlatCategory> Categories { get; set; }
}
public class FlatCategory
{
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
}
我正在尝试将我的平面类别列表映射到一个非常规的结构,如下所示:
public class HieraricalCategoryList
{
public List<Category> Categories { get; set; }
}
public class Category
{
public string ID { get; set; }
public string Name { get; set; }
public string ParentID { get; set; }
public List<Category> ChildCategories { get; set; }
}
我的问题是,实现这一点的最佳方法是什么,因为可能存在无限数量的子层?
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var hieraricalCategoryList = new HieraricalCategoryList();
//Do something here to map the flat category list to the hierarichal one...
return hieraricalCategoryList;
}
答案 0 :(得分:6)
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var categories = (from fc in flatCategoryList.Categories
select new Category() {
ID = fc.ID,
Name = fc.Name,
ParentID = fc.ParentID
}).ToList();
var lookup = categories.ToLookup(c => c.ParentID);
foreach(var c in categories)
{
// you can skip the check if you want an empty list instead of null
// when there is no children
if(lookup.Contains(c.ID))
c.ChildCategories = lookup[c.ID].ToList();
}
return new HieraricalCategoryList() { Categories = categories };
}
答案 1 :(得分:4)
进行此转换的一种非常简单且高性能的方法是创建一个查找,在该查找中将ID值映射到应该是该ID值的子节点的节点。可以在单个节点传递中创建此查找。之后,您可以再次遍历所有节点,将其子集合分配为查找中其ID值的值。
请注意,如果查找映射到您要转换为的类型的对象而不是从中进行转换,则这会更简单。
var lookup = list.Categories
.Select(category => new Category()
{
ID = category.ID,
Name = category.Name,
ParentID = category.ParentID,
})
.ToLookup(category => category.ParentID);
foreach (var category in lookup.SelectMany(x => x))
category.ChildCategories = lookup[category.ID].ToList();
var newList = new HieraricalCategoryList()
{
Categories = lookup[null].ToList(),
};
答案 2 :(得分:1)
使用双程解决方案。这假设完整集合可以适合内存。第一遍扫描平面类别列表,并构建一个按ID索引的类别字典。此时子集合全为空,父属性为null。然后第二遍再次扫描它们,并构建子集合并设置父属性。
未经测试的代码:
var final = new Dictionary<string, Category>();
var rootCategories = new List<Category>();
// Pass 1
foreach (var flat in flatList)
{
Category cat = new Category() { ID = flat.ID, Name = flat.Name, parent = null }
cat.Children = new List<Category>();
final[flat.ID] = cat;
}
// Pass 2
foreach (var flat in flatList)
{
// find myself -- must exist
var self = final[flat.ID];
// find parent -- may not exist
if (final.ContainsKey(flat.ParentID)
{
var parent = final[flat.ParentID];
parent.Children.Add(self);
self.Parent = parent;
}
else
{
rootCategories.Add(self);
}
}
这将有O(n)运行时间,因为它是两次线性扫描,有一些字典查找,即O(1)。
答案 3 :(得分:1)
改进了建议的答案
public HieraricalCategoryList MapCategories(FlatCategoryList flatCategoryList)
{
var categories = (from fc in flatCategoryList.Categories
select new Category() {
ID = fc.ID,
Name = fc.Name,
ParentID = fc.ParentID
}).ToList();
var lookup = categories.ToLookup(c => c.ParentID);
foreach(var c in rootCategories)//only loop through root categories
{
// you can skip the check if you want an empty list instead of null
// when there is no children
if(lookup.Contains(c.ID))
c.ChildCategories = lookup[c.ID].ToList();
}
//if you want to return only root categories not all the flat list
//with mapped child
categories.RemoveAll(c => c.ParentId != 0);//put what ever your parent id is
return new HieraricalCategoryList() { Categories = categories };
}