【问题标题】:Recursively create a tree hierarchy off an array of strings从字符串数组递归创建树层次结构
【发布时间】:2019-12-18 10:20:52
【问题描述】:

我有一个由“!”分隔的字符串数组。我正在尝试分解该字符串并在名为 PivotGroup 的自定义类中递归地创建树层次结构。例如,我的目标是分解字符串数组

 string[] paths = new string[] {
            "ROOT!ZZZ!AAA!EEE!15712",
            "ROOT!ZZZ!AAA!EEE!15722",
            "ROOT!ZZZ!AAA!EEE!13891"}

进入 PivotGroup 类,例如 PivotGroup 包含嵌入数组字符串的ChildGroups[]

例如:

     PivotGroup pgGroup = new PivotGroup();
     pgGroup.ChildGroups[0] = PivotGroup[]; // Key:Book Level 3 Value: "AAA"

现在,在 Book Level 3 ChildGroups 中,我需要设置 Book Level 4 的值为“EEE”,而在“EEE”的 ChildGroups 中,我需要创建另一个在这种情况下大小为 3 的 childGroup 数组,称为 Book Level 5并为以下 15712、15722、13891 中的每一个设置另一个 PivotGroup

这是我的 PivotGroup 类和嵌入式类对象:

public class PivotGroup
{
    public PivotGroup() { }

    public PivotGroup(PivotGroupKey groupKey, PivotRow data, PivotGroup[] childGroups, bool leaf, int groupLevel)
    {
        GroupKey = groupKey;
        Data = data;
        ChildGroups = childGroups;
        Leaf = leaf;
        GroupLevel = groupLevel;
    }

    public PivotGroupKey GroupKey { get; private set; }
    public PivotRow Data { get; private set; }
    public PivotGroup[] ChildGroups { get; set; }
    public bool Leaf { get; private set; }
    public int GroupLevel { get; private set; }

    public override string ToString()
    {
        return GroupKey + ", GroupLevel: " + GroupLevel + ", Children: " +
            ChildGroups.Length + (Leaf ? " (Leaf)" : "");
    }
}

public class PivotGroupKey
{
    public PivotGroupKey()
    {
    }

    public PivotGroupKey(string keyGroup, string keyValue)
    {
        if(keyGroup != null)
            KeyGroup = string.Intern(keyGroup);

        if (keyValue != null)
            KeyValue = string.Intern(keyValue);
    }

    public string KeyGroup { get; private set; }
    public string KeyValue { get; private set; }

    public override string ToString()
    {
        return KeyGroup + ": " + KeyValue;
    }
}

public class PivotRow
{
    public PivotRow()
    {
    }

    public PivotRow(string key, params object[] data) : this(key, true, data) { }

    public PivotRow(string key, bool entitled, params object[] data)
    {
        Data = data;
        Key = null;
        Entitled = entitled;
    }

    public object[] Data { get; private set; }
    public bool Entitled { get; private set; }
    public string Key { get { return null; } set { } }
}

我试过的主程序:

public class BookLevels 
{
    public string Root { get; set; }
    public string BookLevel2 { get; set; }
    public string BookLevel3 { get; set; }
    public string BookLevel4 { get; set; }
    public string BookLevel5 { get; set; }
}

class Program
{
    static void BuildTree(string[] paths)
    {
        var BookPaths = paths.Select(x => x.Split('!'))
            .Select(x => new BookLevels
            {
                Root = x[0],
                BookLevel2 = x[1],
                BookLevel3 = x[2],
                BookLevel4 = x[3],
                BookLevel5 = x[4]
            }).GroupBy(z => new { z.BookLevel3, z.BookLevel4 }).ToArray();


        var BookLevel3Cnt = BookPaths.Select(q => q.Key.BookLevel3).Count();

        PivotGroup root = new PivotGroup(
            new PivotGroupKey("Total", ""),
            new PivotRow(null, new string[8]),
            new PivotGroup[BookLevel3Cnt], false, 0);

        foreach (var booklevel3 in BookPaths)
        {
            AddChildren(root, booklevel3);
        }
    }

    private static void AddChildren(PivotGroup root, IGrouping<object, BookLevels> booklevel, int index = 0)
    {
        root.ChildGroups[index] = new PivotGroup(
        new PivotGroupKey("Book Level " + (index + 3).ToString(), booklevel.Key.ToString()),
        new PivotRow(null, new string[8]),
        AddChildren(root, booklevel[index], index + 1), false, 0);
    }

    static void Main(string[] args)
    {
        string[] paths = new string[] {
            "ROOT!ZZZ!AAA!EEE!15712",
            "ROOT!ZZZ!AAA!EEE!15722",
            "ROOT!ZZZ!AAA!EEE!13891",
            "ROOT!ZZZ!AAA!DDD!15712",
            "ROOT!ZZZ!AAA!DDD!15722",
            "ROOT!ZZZ!AAA!DDD!13891",
            "ROOT!ZZZ!BBB!DDD!15812",
            "ROOT!ZZZ!BBB!DDD!15822",
            "ROOT!ZZZ!BBB!DDD!13891",
        };

        BuildTree(paths);
        Console.WriteLine();
        Console.ReadLine();
    }

我认为我的问题可能是我创建分解字符串的 Linq 语句的方式,因为我不确定如何递归地进行。

【问题讨论】:

    标签: c# linq recursion tree


    【解决方案1】:

    我不确定哪个属性包含什么。另外,为了简单起见并能够专注于递归算法,我重新定义了这样的组类(这并不意味着您必须更改您的类,而是调整我的算法):

    public class PivotGroup
    {
        public string Key { get; set; }
        public List<PivotGroup> ChildGroups { get; } = new List<PivotGroup>();
        public override string ToString() => Key; // Makes debugging easier.
    }
    

    这个想法是路径的值进入键。我制作了ChildGroups 一个列表,以便能够连续添加孩子。我的BuildTree 返回根目录

    static PivotGroup BuildTree(string[] paths)
    {
        var root = new PivotGroup { Key = "ROOT" };
        foreach (string path in paths) {
            AddChildren(root, path.Split('!').Skip(1).ToList());
        }
        return root;
    }
    

    递归部分进入AddChildren。我将路径转换为List&lt;string&gt;,以便能够删除添加的部分。 AddChildren 假定 path 中的第一项是要添加的第一个子项。

    static void AddChildren(PivotGroup group, List<string> path)
    {
        string key = path[0];
        int index = group.ChildGroups.FindIndex(g => g.Key == key);
        PivotGroup child;
        if (index >= 0) { // A child with this key exists.
            child = group.ChildGroups[index]; // Select this existing child.
        } else { // This key is missing. Add a new child.
            child = new PivotGroup { Key = key };
            group.ChildGroups.Add(child);
        }
        if (path.Count > 1) {
            path.RemoveAt(0); // Remove the added child key and add the rest recursively.
            AddChildren(child, path);
        }
    }
    

    我们通过沿着树向下走来添加孩子,并在必要时添加新孩子。

    这会递归地打印树:

    private static void PrintTree(PivotGroup group, int level)
    {
        Console.WriteLine(new String(' ', 2 * level) + group.Key);
        foreach (PivotGroup child in group.ChildGroups) {
            PrintTree(child, level + 1);
        }
    }
    
    string[] paths = new string[] {
        "ROOT!ZZZ!AAA!EEE!15712",
        ...
    };
    
    PivotGroup root = BuildTree(paths);
    PrintTree(root, 0);
    Console.ReadKey();
    

    我们也可以使用循环而不是递归,因为我们一次添加一个分支:

    static PivotGroup BuildTree(string[] paths)
    {
        var root = new PivotGroup { Key = "ROOT" };
        foreach (string path in paths) {
            PivotGroup group = root;
            string[] pathElements = path.Split('!');
            for (int i = 1; i < pathElements.Length; i++) { // Element [0] is ROOT, we skip it.
                string key = pathElements[i];
                int index = group.ChildGroups.FindIndex(g => g.Key == key);
                PivotGroup child;
                if (index >= 0) { // A child with this key exists.
                    child = group.ChildGroups[index]; // Select this existing child.
                } else { // This key is missing. Add a new child.
                    child = new PivotGroup { Key = key };
                    group.ChildGroups.Add(child);
                }
                group = child;
            }
        }
        return root;
    }
    

    List&lt;T&gt;.FindIndex 对于大型列表效率低下。如果您有大型数据集并且顺序无关紧要,请切换到Dictionary&lt;string, PivotGroup&gt;。如果需要对数据进行排序,请使用SortedDictionary&lt;string, PivotGroup&gt;

    【讨论】:

      【解决方案2】:

      这是一些简单的递归代码:

      using System;
      using System.Collections.Generic;
      using System.Linq;
      using System.Text;
      
      namespace ConsoleApplication1
      {
          class Program
          {
              static void Main(string[] args)
              {
                 string[] paths = new string[] {
                  "ROOT!ZZZ!AAA!EEE!15712",
                  "ROOT!ZZZ!AAA!EEE!15722",
                  "ROOT!ZZZ!AAA!EEE!13891"};
      
                  List<List<string>> inputData = paths.Select(x => x.Split(new char[] {'!'}).ToList()).ToList();
      
                  Node root = new Node();
                  Node.ParseTree(root, inputData);
              }
          }
          public class Node
          {
              public string name { get; set; }
              public List<Node> children { get; set; }
      
              public static void ParseTree(Node parent, List<List<string>> inputData)
              {
                  parent.name = inputData.First().FirstOrDefault();
                  var groups = inputData.Select(x => x.Skip(1)).GroupBy(x => x.Take(1).FirstOrDefault());
                  foreach (var group in groups)
                  {
                      if (group.Key != null)
                      {
                          if (parent.children == null) parent.children = new List<Node>();
                          Node newNode = new Node();
                          parent.children.Add(newNode);
                          ParseTree(newNode, group.Select(x => x.Select(y => y).ToList()).ToList());
                      }
                  }
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-02-10
        • 2012-12-14
        • 2017-04-18
        • 1970-01-01
        • 1970-01-01
        • 2023-03-24
        • 2013-06-07
        • 1970-01-01
        相关资源
        最近更新 更多