通过C#中的递归将列表<t>转换为结构化列表<t> </t> </t>

时间:2014-12-10 08:25:40

标签: c# generics recursion

我有一个现有的课程:

public class Product
{
 public int Id { get; set; }
 public int ParentId { get; set; }
 public string Info { get; set; }
}

我也有一个给定的List<Product>

var Lst = new List<Product>();
Lst.Add(new Product{ Id=1,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=2,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=3,ParentId=0,Info="a"});
Lst.Add(new Product{ Id=60,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=61,ParentId=1,Info="a"});
Lst.Add(new Product{ Id=62,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=72,ParentId=61,Info="a"});
Lst.Add(new Product{ Id=90,ParentId=2,Info="a"});

可视化:

1
|
+---60
|
+---61
     |
     +---62
     |
     +---72

2
|
+---90

3

如您所见,List<> flat 。 (所有项目都在列表中的同一级别。它只是id,parentId代表层次结构)

现在 - 我需要创建结构List<>,以便List中的每个项目 in 其父对象:

所以我创建了一个额外的结构类来保存这个结构:

public class Node
{
 public Product Product { get; set; }
 public List<Node> LstNodes  { get; set; }
}

现在我可以做到:

List<Node> lstNodes = new List<Node>();

最初我可以添加根源:

lstNodes=Lst.Where(product=>product.ParentId==0).Select(node=>new Node{Product=node}).ToList();

现在我可以开始递归,与父母一起插入项目。

那么问题在哪里?

问题:

我想首先避免插入根元素(root是ParentId=0)。

使用一个递归方法(包括根)有没有办法做到这一点?

期望的结果:lstNodes中的3个节点,其中每个节点递归地具有其子节点。

3 个答案:

答案 0 :(得分:4)

这样的东西?

List<Node> GetNodes(List<Product> lst, int parentId = 0)
{
    var childProducts = lst.Where(x=>x.ParentId == parentId);
    return childProducts
           .Select(x=> new Node { Product = x, LstNodes = GetNodes(lst, x.Id)}
           .ToList();
}

纯粹的通用版本:

class Node<T>
{
    public T Item { get; set; }
    public List<Node<T>> LstNodes  { get; set; }
}

List<Node<T>> GetNodes<T>(List<T> lst, Func<T, int> idSelector, Func<T, int> parentIdSelector, int parentId = 0)
{
    var childProducts = lst.Where(x=>parentIdSelector(x) == parentId);
    return childProducts
           .Select(x=> new Node<T> { Item = x, LstNodes = GetNodes<T>(lst, idSelector,parentIdSelector, idSelector(x))})
           .ToList();
}

GetNodes(Lst,x=>x.Id,x=>x.ParentId, 0);

答案 1 :(得分:1)

你可以试试这个:

    static List<Node> BuildTree(List<Product> lst, int topID) 
    {
        var tree = Enumerable.Empty<Node>().ToList();
        if (lst == null)
        {
            return tree;
        }

        foreach (var product in lst)
        {
            if (product.ParentId == topID)
            {
                Node node = new Node 
                {
                    Product = product,
                    LstNodes = BuildTree(lst.Where(p => p.ParentId == product.Id).ToList(), product.Id)
                };

                tree.Add(node);
            }
        }

        return tree;
    }

您可以致电:List<Node> roots = BuildTree(Lst, 0);

答案 2 :(得分:1)

到目前为止提出的解决方案有效,但每个节点迭代整个集合一次 这使得它们O(n2)并且不适合大型集合(数据库,......) 这是一个以线性时间运行的解决方案:

var products = new List<Product>();
products.Add(...);
...

var nodes          = products.Select(p => new Node { Product = p }).ToList(); // 1
var nodeWithId     = nodes.ToDictionary(n => n.Product.Id);                   // 2
var parentChildren = nodes.Where(n => n.Product.ParentId != 0)
                          .ToLookup(n => nodeWithId[n.Product.ParentId]);     // 3

foreach (var pc in parentChildren)  // 4
{
    var parent   = pc.Key;
    var children = pc.ToList();

    parent.LstNodes = children;
}

说明:

  1. 从产品中创建节点。
  2. 创建一个字典,允许我们轻松找到具有给定ID的节点
  3. 创建一个查找(每个键具有多个值的字典),它为我们提供给定节点的子节点。
  4. 重复查找与子女及其父母的关联