【问题标题】:Creating a Nested List from a Flat List in C#在 C# 中从平面列表创建嵌套列表
【发布时间】:2018-06-05 10:19:44
【问题描述】:

我目前有以下课程:

public class NavigationItem
{
    public int ID { get; set; }
    public string Title { get; set; }
    public int ParentID { get; set; }
    public List<NavigationItem> Children { get; set; }
}

public class FlatItem
{
    public int ID { get; set; }
    public string Title { get; set; }
    public int ParentID { get; set; }
}

我有一个样本数据如下:

+====+============+==========+
| ID |   Title    | ParentID |
+====+============+==========+
|  1 | Google     |          |
+----+------------+----------+
|  2 | Microsoft  |          |
+----+------------+----------+
|  3 | Oracle     |          |
+----+------------+----------+
|  4 | Gmail      |        1 |
+----+------------+----------+
|  5 | Sheets     |        1 |
+----+------------+----------+
|  6 | Adsense    |        1 |
+----+------------+----------+
|  7 | Azure      |        2 |
+----+------------+----------+
|  8 | SharePoint |        2 |
+----+------------+----------+
|  9 | Office     |        2 |
+----+------------+----------+
| 10 | Java       |        3 |
+----+------------+----------+
| 11 | Word       |        9 |
+----+------------+----------+
| 12 | Excel      |        9 |
+----+------------+----------+
| 13 | PowerPoint |        9 |
+----+------------+----------+

我已经有了从上面的示例数据中提取所有信息并将其转换为List&lt;FlatItem&gt; 对象的代码。

最好的方法是什么,这样我就可以拥有一个 List&lt;NavigationItem&gt; 对象,如下所示:

  • 谷歌
    • Gmail
    • 床单
    • AdSense
  • 微软
    • 天蓝色
    • SharePoint
    • 办公室
      • Excel
      • PowerPoint
  • 甲骨文
    • Java

我正在考虑创建一个递归方法来循环遍历我的List&lt;FlatItem&gt;,然后将其构建为 NavigationItem 的嵌套列表。

【问题讨论】:

  • 你有没有试过的代码不起作用?
  • ParentID int 还是int??您的表格显示它是int?,但您的代码显示int

标签: c# .net


【解决方案1】:

不需要递归。您可以使用 LINQ 轻松构建结构:

List<FlatItem> flatItems = ...;

var navigationItems = flatItems.Select(
    i => new NavigationItem { ID = i.ID, Title = i.Title, ParentID = i.ParentID }
).ToList();

foreach (var i in navigationItems)
    i.Children = navigationItems.Where(n => n.ParentID == i.ID).ToList();

// get Google, Microsoft, Oracle items
var rootNavigationItems = navigationItems.Where(n => n.ParentID == 0);

【讨论】:

  • 这没有给出正确的输出。你测试了吗?
  • @Enigmativity 是的,它有效,不幸的是我忘记了最后一行,修复它。
【解决方案2】:

如果你可以使用递归,你可以创建一个这样的函数:

public List<NavigationItem> ChildrenOf(List<FlatItem> flatItems, int parentId)
{
    var childrenFlatItems = flatItems.Where(i => i.ParentID == parentId);
    return childrenFlatItems.Select(i => new NavigationItem {
        ID = i.ID,
        Title = i.Title,
        ParentID = i.ParentID, 
        Children = ChildrenOf(flatItems, i.ID)})
    .ToList();
}

然后,假设您的根项目的父 id 为 0(因为您没有使用可空类型),您生成完整列表:

ChildrenOf(flatsItems, 0)

【讨论】:

    【解决方案3】:

    未经测试,但你可以试试这个,应该也相当快

    var list = new List<FlatItem>();
    var result = new List<NavigationItem>();
    
    // just a helper to remember ids
    var dict = new Dictionary<int, NavigationItem>();
    
    foreach (var item in list)
    {
       var nav = new NavigationItem()
                      {
                         ID = item.ID,
                         ParentID = item.ParentID,
                         Title = item.Title,
                         Children = new List<NavigationItem>()                                   
                      };
    
       if (!dict.ContainsKey(nav.ParentID))
          result.Add(nav);       
       else
          dict[nav.ParentID].Children.Add(nav);
    
       dict.Add(item.ID, nav);
    }
    

    【讨论】:

      【解决方案4】:

      试试这个:

      List<FlatItem> source = new List<UserQuery.FlatItem>()
      {
          new FlatItem() { ID = 1, Title = "Google", ParentID = null },
          new FlatItem() { ID = 2, Title = "Microsoft", ParentID = null },
          new FlatItem() { ID = 3, Title = "Oracle", ParentID = null },
          new FlatItem() { ID = 4, Title = "Gmail", ParentID = 1 },
          new FlatItem() { ID = 5, Title = "Sheets", ParentID = 1 },
          new FlatItem() { ID = 6, Title = "Adsense", ParentID = 1 },
          new FlatItem() { ID = 7, Title = "Azure", ParentID = 2 },
          new FlatItem() { ID = 8, Title = "SharePoint", ParentID = 2 },
          new FlatItem() { ID = 9, Title = "Office", ParentID = 2 },
          new FlatItem() { ID = 10, Title = "Java", ParentID = 3 },
          new FlatItem() { ID = 11, Title = "Word", ParentID = 9 },
          new FlatItem() { ID = 12, Title = "Excel", ParentID = 9 },
          new FlatItem() { ID = 13, Title = "PowerPoint", ParentID = 9 },
      };
      
      var lookup = source.ToLookup(x => x.ParentID);
      
      Func<int?, List<NavigationItem>> build = null;
      build = pid =>
          lookup[pid]
              .Select(x => new NavigationItem()
              {
                  ID = x.ID,
                  Title = x.Title,
                  ParentID = x.ParentID,
                  Children = build(x.ID)
              })
              .ToList();
      

      要启动进程调用build(null)。这给了我这个:

      这确实假设 ParentId 属性是 int? - 您的数据表确实表明了这一点。

      【讨论】:

        【解决方案5】:

        没有递归,只有GroupBy

        List<NavigationItem> list = ... // map from List<FlatItem> 
        // and init Children = new List<NavigationItem>();
        
        var groups = list.GroupBy(x => x.ParentID).ToList();
        foreach (var g in groups)
        {
            var items = list.Find(x => x.ID == g.Key);
            if (items != null) 
                items.Children = g.ToList();
        }
        
        // tops is [Google, Microsoft, Oracle]
        var tops = list.Where(x => x.ParentID == null).ToList();
        

        【讨论】:

          猜你喜欢
          • 2011-06-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2022-01-19
          相关资源
          最近更新 更多