【问题标题】:Convert a flat list to a multi level hierarchy将平面列表转换为多级层次结构
【发布时间】:2018-09-28 22:30:21
【问题描述】:

我有以下需要转换为多级分层树。

public enum ERootType { NotUsed, RootType1, RootType2, }
public enum ESubType { NotUsed, SubTypeA, SubTypeB, SubTypeC, }

public class Payload { }

public class MyData
{
    public MyData(ERootType rootType, ESubType subType,
         string displayName, Payload payload)
    {
        RootType = rootType;
        SubType = subType;
        DisplayName = displayName;
        Payload = payload;
    }

    public ERootType RootType { get; }
    public ESubType SubType { get; }
    public string DisplayName { get; set; }
    public object Payload { get; }

    public object this[string propertyName]
    {
        get
        {
            Type myType = typeof(MyData);
            PropertyInfo myPropInfo = myType.GetProperty(propertyName);
            return myPropInfo.GetValue(this, null);
        }
        set
        {
            Type myType = typeof(MyData);
            PropertyInfo myPropInfo = myType.GetProperty(propertyName);
            myPropInfo.SetValue(this, value, null);
        }
    }
}

// Create and initialize flat list of data
var Resources = new List<MyData>()
{
    new MyData(ERootType.RootType1, ESubType.SubTypeA, "Item1", new Payload()),
    new MyData(ERootType.RootType1, ESubType.SubTypeA, "Item2", new Payload()),
    new MyData(ERootType.RootType1, ESubType.SubTypeB, "Item3", new Payload()),
    new MyData(ERootType.RootType1, ESubType.SubTypeB, "Item4", new Payload()),
    new MyData(ERootType.RootType1, ESubType.SubTypeC, "Item5", new Payload()),
    new MyData(ERootType.RootType2, ESubType.SubTypeA, "Item6", new Payload()),
    new MyData(ERootType.RootType2, ESubType.SubTypeA, "Item7", new Payload()),
    new MyData(ERootType.RootType2, ESubType.SubTypeB, "Item8", new Payload()),
    new MyData(ERootType.RootType2, ESubType.SubTypeB, "Item9", new Payload()),
    new MyData(ERootType.RootType2, ESubType.SubTypeC, "Item10", new Payload()),
};

我需要将上述数据转换为如下所示的分层列表。

  • RootType1(ERootType = RootType1,ESubType = NotUsed,DiaplayName = "RootType1",Payload = null)
    • SubTypeA (ERootType = NotUsed, ESubType = SubTypeA, DiaplayName = "SubTypeA", Payload = null)
      • Item1(ERootType = RootType1,ESubType = SubTypeA,DiaplayName = "Item1",Payload = 有效负载对象实例)
      • 项目2
    • 子类型B
      • 项目 3
      • 项目4
    • SubTypeC
      • 项目5
  • RootType2
    • 子类型A
      • 项目6
      • 项目7
    • 子类型B
      • 项目8
      • 项目9
    • SubTypeC
      • Item10

我试图想出一个使用泛型和 Linq 的解决方案。我创建了以下类来支持分层结果。

public class TreeItem<T>
{
    public T Item { get; set; }
    public IEnumerable<TreeItem<T>> Children { get; set; }
}

我在想最好有一个通用方法,它可以通过传入一个级别数组来生成具有 n 个级别的树,这些级别是正在转换的类中的属性的名称。我在想这种方法会使用递归。这是我正在使用的函数声明。

public static IEnumerable<TreeItem<T>> GenerateNLevelTree<T>(this IEnumerable<T> collection,
    string[] levelProps, int currentLevel = 0)

类 MyData 包含一个索引器,以支持使用字符串访问 MyData 的每个属性。

对于生成分层数据结构的解决方案的任何帮助将不胜感激。

【问题讨论】:

  • 请尽量以json或xml格式给出你的数据原型。
  • 我没有在这个网站上发布很多问题,我也没有对 json 做太多事情。您是否有关于如何执行此操作的快速说明,或者您能否提供有关如何执行此操作的链接。谢谢。
  • 您可以使用 group by cluase 来构建您的层次结构。但是要达到 3 级或 4 级,这很复杂。
  • 这看起来可能有用:stackoverflow.com/questions/847066/group-by-multiple-columns。阅读所有答案,而不仅仅是接受或高票的答案
  • 我大大更新了我的答案。我相信它现在可以满足您的需求。

标签: c#


【解决方案1】:

这可能会有所帮助。如果你这样做:

   var grouped1 = Resources.GroupBy(x => new {x.RootType, x.SubType, x.DisplayName});
   var grouped2 = grouped1.GroupBy(x => new {x.Key.RootType, x.Key.SubType});
   var grouped3 = grouped2.GroupBy(x => new {x.Key.RootType});

如果您遍历 grouped3 中的集合集合,您将获得与您描述的相似的层次结构。您可能希望将其转换为对您的应用程序而言在语义上更合理的内容。

顺便说一句,感谢您的精彩再现。包括了尝试此操作所需的所有代码。这在这里非常罕见。

更新

我为导航 grouped3 所做的事情是:

  1. 首先创建一堆类来保存层次结构的位。

它们都遵循相同的模式:

 public class RootNode
 {
     private readonly List<SubNode> _subList = new List<SubNode>();
     public ERootType RootType { get; set; }
     public IEnumerable<SubNode> Subs => _subList;

     public RootNode(ERootType rootType)
     {
         RootType = rootType;
     }

     public void AddSub(SubNode subNodeToAdd)
     {
         _subList.Add(subNodeToAdd);
     }
 }

public class SubNode
{
    private readonly List<ItemNode> _itemList = new List<ItemNode>();
    public ESubType SubType { get; set; }
    public IEnumerable<ItemNode> Items => _itemList;

    public SubNode(ESubType subType)
    {
        SubType = subType;
    }

    public void AddItem(ItemNode itemToAdd)
    {
        _itemList.Add(itemToAdd);
    }
}

public class ItemNode
{
    private readonly List<Payload> _payloadList  = new List<Payload>();

    public string Item { get; set; }
    public IEnumerable<Payload> Payloads => _payloadList;
    public ItemNode(string item)
    {
        Item = item;
    }

    public void AddPayload(Payload payloadToAdd)
    {
        _payloadList.Add(payloadToAdd);
    }
}
  1. 然后,在原代码中grouped3的赋值之后,我添加了这个。

它只是遍历grouped3 混乱并在树中创建一堆节点:

    var roots = new List<RootNode>();

    foreach (var root in grouped3)
    {
        var rootNode = new RootNode(root.Key.RootType);
        roots.Add(rootNode);
        foreach (var sub in root)
        {
            var subNode = new SubNode(sub.Key.SubType);
            rootNode.AddSub(subNode);
            foreach (var item in sub)
            {
                var itemNode = new ItemNode(item.Key.DisplayName);
                subNode.AddItem(itemNode);
                foreach (var payload in item)
                {
                    itemNode.AddPayload(payload.Payload as Payload);
                }
            }
        }
    }

当我查看调试器时,我看到了一个强类型树。不要害怕匿名类型。 var 关键字是你的朋友(嗯,那个和调试器)。

【讨论】:

  • 我将您的代码插入到我的项目中,当使用调试器查看它时,我看到了我正在寻找的层次结构,但这会生成我不熟悉遍历的匿名类型。我的最终目标是用这些数据填充 TreeView 控件,因此我需要像上面显示的 TreeItem 类这样的结构中的数据。非常感谢一个如何将数据放入我需要的结构中的示例。
  • 这让我非常接近我需要的东西。感谢您花费时间和精力帮助我。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-02-06
  • 1970-01-01
  • 2023-03-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-23
相关资源
最近更新 更多