搜索类的层次结构并返回路径以到达那里

时间:2019-02-20 20:27:45

标签: c#

我有这个课程,一个类别层次结构。

class Categories
{
    public long Id { get; set; }

    public long ParentId { get; set; }

    public string Name { get; set; }

    public bool IsActive { get; set; }

    public List<Categories> ChildrenData { get; set; }
}

我如何递归地遍历此类未知深度的类并返回到达那里的路径?

所有“ Id”值都是唯一的。假设我想找到Id = 23,并通过连接“名称”来获取到达该位置的路径。

例如,在下面的图像中,搜索ID = 23将返回:默认类别/图书/非小说/畅销书

Example Hierarchy

3 个答案:

答案 0 :(得分:5)

我的建议是您首先建立一个索引:

public static Dictionary<long, Category> IndexBuilder(Category c)
{
  var index = new Dictionary<long, Category>();
  IndexBuilder(c, index);
  return index;
}

private static void IndexBuilder(Category c, Dictionary<long, Category> index)
{
  if (index.ContainsKey(c.Id))
    return;
  index[c.Id] = c;
  foreach(var child in c.ChildrenData)
    IndexBuilder(child, index);
}

现在您可以进行查找,然后轻松生成路径:

static IEnumerable<Category> PathToRoot(long id, Dictionary<long, Category> index)
{
  // Presumably the parent id of the top category is a sentinel.
  long current = id
  while (current != 0)
  {
    var category = index[current];
    yield return category;
    current = category.ParentId;
  }
}

或者也许我们只是走到索引用完为止:

static IEnumerable<Category> PathToRoot(long id, Dictionary<long, Category> index)
{
  long current = id
  while (index.ContainsKey(current))
  {
    var category = index[current];
    yield return category;
    current = category.ParentId;
  }
}

现在您有了一个可用来制作字符串的工具:

static string Slash<T>(this IEnumerable<T> items) =>
  string.Join("/", items);

var s = PathToRoot(23, index)
  .Reverse()
  .Select(c => c.Name)
  .Slash();

看看我在这里做什么? 制作一堆辅助方法,每个方法大约长五行,可以将它们组合在一起以提供强大的解决方案。

答案 1 :(得分:1)

我提供了两种方法,第一种是递归的,最后一种不是。

以递归的方式,添加对父母的引用。这样,当您找到匹配项时,您可以轻松地沿链条上的路径进行遍历以创建路径。

class Categories
{
   public Categories Parent { get; set; }

   public long Id { get; set; }

   public long ParentId { get; set; }

   public string Name { get; set; }

   public bool IsActive { get; set; }

   public List<Categories> ChildrenData { get; set; }
}

然后添加一个Find()方法:

public string Find(long id)
{
   if( Id == id ) 
   {
    return GetPath(); //<-- we need to code this next. 
   }
   else
   {
      foreach( var entry in Categories)
      {
         string path = entry.Find(id);
         if( path != null )
         {
             return path;
         }
      }
      return null;
   }
}

最后是GetPath(),这里的假设是类别的最高级别实例没有父级:

public string GetPath()
{
    System.Text.StringBuilder sb = new StringBuilder();
    Categories current = this;
    while( current != null)
    {
        sb.Insert(0,current.Name);        
        if( current != this)
        {
           sb.Insert(0,"/");        
        }
        current = Parent;
    }
    return sb.ToString();
}

现在,如果递归不是您想要的,则将当前路径传递到Find()方法。

public string Find(long id, string pathSoFar)
{
    if (pathSoFar == null)
    {
       pathSoFar = Name;
    }
    else
    {
       pathSoFar = pathSoFar + Name;
    }

    if ( Id == id)
    {
       return pathSoFar;
    }
    else
    {
      foreach( var entry in Categories)
      {
         string path = entry.Find(id, pathSoFar + "/");
         if( path != null )
         {
             return path;
         }
      }
      return null;
    }
}

用法:

var nonRecusive = cats.Find(23, null);

答案 2 :(得分:0)

这将获得您使用递归查找的内容:

void Main()
{
    var data = GetData();

    Console.WriteLine(GetPath(data, 23, ""));
}

public String GetPath(Categories c, Int32 id, String path)
{   
    if (c.Id == id)
    {
        return path + "/" + c.Name;
    }

    foreach (var cd in c.ChildrenData)
    {
        var p = GetPath(cd, id, path + "/" + c.Name);
        if (!String.IsNullOrWhiteSpace(p))
        {
            return p;
        }
    }
    return "";
}

public class Categories
{
    public long Id { get; set; }

    public long ParentId { get; set; }

    public string Name { get; set; }

    public bool IsActive { get; set; }

    public List<Categories> ChildrenData { get; set; }
}

public Categories GetData()
{
    return
        new Categories
        {
            Id = 1,
            Name = "Default Category",
            ChildrenData = new List<Categories>
            {
                    new Categories
                    {
                        Id = 2,
                        Name = "Magazines",
                        ChildrenData = new List<Categories> {}
                    },
                    new Categories
                    {
                        Id = 2,
                        Name = "Books",
                        ChildrenData = new List<Categories>
                        {
                            new Categories
                            {
                                Id = 20,
                                Name = "Fiction",
                                ChildrenData = new List<Categories> {}
                            },
                            new Categories
                            {
                                Id = 21,
                                Name = "Nonfiction",
                                ChildrenData = new List<Categories>
                                {
                                    new Categories
                                    {
                                        Id = 22,
                                        Name = "New",
                                        ChildrenData = new List<Categories> {}
                                    },
                                    new Categories
                                    {
                                        Id = 23,
                                        Name = "Best-Sellers",
                                        ChildrenData = new List<Categories> {}
                                    },
                                }
                            }


                        }
                    }
            }
        };
}