使用父ID C#将平面列表映射到分层列表

时间:2014-08-27 16:21:25

标签: c#

我有一个平面的类别列表,如以下类所示

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

4 个答案:

答案 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 };
}