在C#中获取父级的子级和子级

时间:2018-08-16 12:29:12

标签: c# linq

我想基于列表数据生成一些字符串:

提供以下课程:

public class Item
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int ParentId { get; set; }
}

我有以下相关Item对象的列表:

var list = new List<Item>();
list.Add(new Item { Id = 1, Name = "Parent1", ParentId = 0 });
list.Add(new Item { Id = 2, Name = "Child1", ParentId = 1 });
list.Add(new Item { Id = 3, Name = "Child2", ParentId = 1 });
list.Add(new Item { Id = 4, Name = "GrandChild1", ParentId = 2 });
list.Add(new Item { Id = 5, Name = "GrandChild2", ParentId = 2 });
list.Add(new Item { Id = 6, Name = "GrandChild3", ParentId = 3 });
list.Add(new Item { Id = 7, Name = "GrandChild4", ParentId = 3 });

list.Add(new Item { Id = 8, Name = "Parent2", ParentId = 0 });
list.Add(new Item { Id = 9, Name = "Child1", ParentId = 8 });
list.Add(new Item { Id = 10, Name = "Child2", ParentId = 8 });
list.Add(new Item { Id = 11, Name = "GrandChild1", ParentId = 9 });
list.Add(new Item { Id = 12, Name = "GrandChild2", ParentId = 9 });
list.Add(new Item { Id = 13, Name = "GrandChild3", ParentId = 10 });
list.Add(new Item { Id = 14, Name = "GrandChild4", ParentId = 10 });

现在,我想创建像这样的字符串:

"Parent1:Child1:GrandChild1"
"Parent1:Child1:GrandChild2"
"Parent1:Child2:GrandChild3"
"Parent1:Child2:GrandChild4"

"Parent2:Child1:GrandChild1"
"Parent2:Child1:GrandChild2"
"Parent2:Child2:GrandChild3"
"Parent2:Child2:GrandChild4"

我已经尝试过了:

private IList<Item> GetChild(int id, IList<Item> items)
{
    var childs = items
        .Where(x => x.ParentId == id || x.Id == id)
        .Union(items.Where(x => x.ParentId == id)
        .SelectMany(y => GetChild(y.Id, items)));

    return childs.ToList();
}

此代码返回parent以及所有子级和子级子级,但是我无法从中获得所需的字符串。

那么我该如何使用LINQ或foreach循环来做到这一点?

4 个答案:

答案 0 :(得分:1)

通过一些联接,您可以获得预期的输出。

Working Fiddle

public void WriteStrings() 
{
    List<Item> items = GetItems();
    IEnumerable<string> resultingStrings = from parent in items.Where(x => x.ParentId == 0)
          join child in items on parent.Id equals child.ParentId
          join grandChild in items on child.Id equals grandChild.ParentId
        select string.Format("{0}:{1}:{2}", parent.Name, child.Name, grandChild.Name);

    foreach(var item in resultingStrings)
        Console.WriteLine(item);
}

输出

Parent1:Child1:GrandChild1
Parent1:Child1:GrandChild2
Parent1:Child2:GrandChild3
Parent1:Child2:GrandChild4
Parent2:Child1:GrandChild1
Parent2:Child1:GrandChild2
Parent2:Child2:GrandChild3
Parent2:Child2:GrandChild4

答案 1 :(得分:0)

以下代码与递归方法遍历列表并打印所需内容。以list为参数调用Traverse方法。

public void Traverse(List<Item> list)
{
    var roots = list.Where(e => e.ParentId == 0).ToList();

    foreach (var item in roots)
    {
        Traverse(list, item);
        Console.WriteLine();
    }
}

private void Traverse(List<Item> list, Item target, string str = "")
{
    str += target.Name;

    var children = list.Where(e => e.ParentId == target.Id).ToList();

    if (!children.Any())
    {
        Console.WriteLine(str);
        return;
    }

    str += ":";

    foreach (var item in children)
    {
        Traverse(list, item, str);
    }
}

答案 2 :(得分:0)

以下是我在评论中提到的两种方法。首先确定叶子,然后为每个叶子工作回到根,建立字符串:

// Build a dictionary of items
var itemsById = list.ToDictionary(i => i.Id);
// Build a dictionary of child items of each node; 0 = root
var childrenById = list.GroupBy(i => i.ParentId).ToDictionary(g => g.Key);

// Find leaf nodes
var leaves = list.Where(i => !childrenById.ContainsKey(i.Id));
// For each leaf, build up a list of parents up to the root then print
foreach (var leaf in leaves)
{
    var queue = new LinkedList<Item>();
    var cursor = leaf;
    do
    {
        // NB this will get stuck if there's a cycle in your tree.
        // You might want to guard against this!
        queue.AddFirst(cursor);
    }
    while (itemsById.TryGetValue(cursor.ParentId, out cursor));

    Console.WriteLine(String.Join(":", queue.Select(i => i.Name)));
}

或者,构建一棵树并通过以下方式递归:

// Build a dictionary of child items of each node; 0 = root
var childrenById = list.GroupBy(i => i.ParentId).ToDictionary(g => g.Key);

// Iterate through tree recursively
var path = new LinkedList<Item>();
var output = new List<String>();
Recurse(0, path, childrenById, output);

static void Recurse(int parentId, LinkedList<Item> path,
    Dictionary<int, IGrouping<int, Item>> childrenById, List<String> output)
{
    if (childrenById.ContainsKey(parentId))
    {
        foreach (var item in childrenById[parentId])
        {
            path.AddLast(item);
            Recurse(item.Id, path, childrenById, output);
            path.RemoveLast();
        }
    }
    else
    {
        // This is a leaf node
        output.Add(String.Join(":", path.Select(i => i.Name)));
    }
}

答案 3 :(得分:0)

尝试以下代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;


namespace ConsoleApplication1
{
    public class Program
    {
        public static void Main()
        {

            Item item = new Item();
            item.CreateList();
            Item.Recursive(0, new List<string>());

            foreach(string descendant in Item.descendants)
            {
                Console.WriteLine(descendant);
            }
            Console.ReadLine();

        }
    }
    public class Item
    {
        public static List<string> descendants = new List<string>();

        public static List<Item> items = null;

        public int Id { get; set; }
        public string Name { get; set; }
        public int ParentId { get; set; }

        public void CreateList()
        {
            items = new List<Item>() {
               new Item { Id = 1, Name = "Parent1", ParentId = 0 },
               new Item { Id = 2, Name = "Child1", ParentId = 1 },
               new Item { Id = 3, Name = "Child2", ParentId = 1 },
               new Item { Id = 4, Name = "GrandChild1", ParentId = 2 },
               new Item { Id = 5, Name = "GrandChild2", ParentId = 2 },
               new Item { Id = 6, Name = "GrandChild3", ParentId = 3 },
               new Item { Id = 7, Name = "GrandChild4", ParentId = 3 },
               new Item { Id = 8, Name = "Parent2", ParentId = 0 },
               new Item { Id = 9, Name = "Child1", ParentId = 8 },
               new Item { Id = 10, Name = "Child2", ParentId = 8 },
               new Item { Id = 11, Name = "GrandChild1", ParentId = 9 },
               new Item { Id = 12, Name = "GrandChild2", ParentId = 9 },
               new Item { Id = 13, Name = "GrandChild3", ParentId = 10 },
               new Item { Id = 14, Name = "GrandChild4", ParentId = 10 }
            };
        }

        public static void Recursive(int parentId, List<string> ancestors)
        {
            List<Item> children = items.Where(x => x.ParentId == parentId).ToList();

            if (children.Count  == 0)
            {
                descendants.Add(string.Join(":", ancestors));
            }
            else
            {
                foreach (Item child in children)
                {
                    List<String> newAncestors = new List<string>(ancestors);
                    newAncestors.Add(child.Name);
                    Recursive(child.Id, newAncestors);
                }
            }
        }
    }
}