C# linq 层次结构树

本文关键字:层次结构 linq | 更新日期: 2023-09-27 18:36:31

标签类由 ID 名称和List<Tagging>组成:

public class Tag
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Tagging> Tagging { get; set; }
}

标记类 :

public class Tagging
{
    public int Id { get; set; }
    [ForeignKey("ParentTag")]
    public int ParentId { get; set; }
    public Tag ParentTag { get; set; }
    [ForeignKey("ChildTag")]
    public int ChildId { get; set; }
    public Tag ChildTag { get; set; }
}
标签

类只是表达标签之间的多对多关系,用于分层目的。例如给定一个列表:

List<Tag> tags = new List<Tag>();
var parent = new Tag {Name = "Parent", Id = 1, Tagging = new List<Tagging>{ new Tagging{ ParentId = 1, ChildId = 2}}};
var child = new Tag {Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3 }}};
var grandChild = new Tag {Name = "GrandChild", Id = 3};
tags.Add(parent);
tags.Add(child);
tags.Add(grandChild);

我正在尝试遍历与他父级相关的所有分层对象。例如,如果调用方法getAllHiearchyObject(Tag parent)输出应该是这样的:

Name : "Parent", Id = 1;
Name : "Child", Id : 2;
Name : "GrandChild", Id :3 

我需要getAllHiearchyObject(Tag parent)的实际实现

C# linq 层次结构树

这个怎么样...

static IEnumerable<Tag> FlattenTag(Tag root)
{
    yield return root;
    if (root.Tagging != null)
        foreach (var childTagging in root.Tagging)
            if (childTagging.ChildTag != null)
                foreach (var grandChildTag in FlattenTag(childTagging.ChildTag))
                    yield return grandChildTag;
}

请注意,上面的第二个foreach允许使用带有递归的yield

用法。。。

foreach(var tag in FlattenTag(root))
...
一个

孩子只有一个父母。

对于只有一个父子关系的简单情况,您可以创建如下方法:

public static class EnumerableExtensions
{
    #region Methods
    public static IEnumerable<T> Unwind<T>(T first, Func<T, T> getNext) 
        where T : class
    {
        if (getNext == null)
            throw new ArgumentNullException(nameof(getNext));
        return Unwind(
            first: first,
            getNext: getNext,
            isAfterLast: item => 
                item == null);
    }
    public static IEnumerable<T> Unwind<T>(
        T first,
        Func<T, T> getNext,
        Func<T, Boolean> isAfterLast)        
    {
        if (getNext == null)
            throw new ArgumentNullException(nameof(getNext));
        if (isAfterLast == null)
            throw new ArgumentNullException(nameof(isAfterLast));
        var current = first;
        while(!isAfterLast(current))
        {
            yield return current;
            current = getNext(current);
        }
    }
    #endregion
}

并按以下方式使用它们(我已经在标记中设置了 ChildTag,因为它将由 EF 完成):

List<Tag> tags = new List<Tag>();
var grandChild = new Tag { Name = "GrandChild", Id = 3 };
var child = new Tag { Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3, ChildTag = grandChild } } };
var parent = new Tag { Name = "Parent", Id = 1, Tagging = new List<Tagging> { new Tagging { ParentId = 1, ChildId = 2, ChildTag = child } } };
tags.Add(parent);
tags.Add(child);
tags.Add(grandChild);
var fromParent = EnumerableExtensions
    .Unwind(
        parent,
        item =>
            item?.Tagging?.FirstOrDefault()?.ChildTag)
    .ToArray();
Console.WriteLine("Parent to child:");
foreach (var item in fromParent)
{
    Console.WriteLine(item);
}

多孩子的适当父母

为了正确创建树,您必须使用:

public class UnwoundItem<T> : IEnumerable<UnwoundItem<T>>
{
    private readonly T _item;
    private readonly IEnumerable<UnwoundItem<T>> _unwoundItems;
    public UnwoundItem(T item, IEnumerable<UnwoundItem<T>> unwoundSubItems)
    {
        this._item = item;
        this._unwoundItems = unwoundSubItems ?? Enumerable.Empty<UnwoundItem<T>>();
    }
    public T Item
    {
        get
        {
            return this._item;
        }
    }
    public IEnumerable<UnwoundItem<T>> UnwoundSubItems
    {
        get
        {
            return this._unwoundItems;
        }
    }
    public IEnumerator<UnwoundItem<T>> GetEnumerator()
    {
        return this._unwoundItems.GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

public static class EnumerableExtensions
{
    #region Methods
    public static UnwoundItem<T> UnwindMany<T>(
        T first,
        Func<T, IEnumerable<T>> getNext)
        where T : class
    {
        if (getNext == null)
            throw new ArgumentNullException(nameof(getNext));
        return UnwindMany(
            first: first,
            getNext: getNext,
            isAfterLast: collection =>
                collection == null);
    }
    public static UnwoundItem<T> UnwindMany<T>(
        T first,
        Func<T, IEnumerable<T>> getNext,
        Func<IEnumerable<T>, Boolean> isAfterLast)
    {
        if (getNext == null)
            throw new ArgumentNullException(nameof(getNext));
        if (isAfterLast == null)
            throw new ArgumentNullException(nameof(isAfterLast));
        var currentItems = getNext(first);
        if (isAfterLast(currentItems))
            return new UnwoundItem<T>(
                item: first, 
                unwoundSubItems: Enumerable.Empty<UnwoundItem<T>>());
        return new UnwoundItem<T>(
            item: first,
            unwoundSubItems: currentItems
                .Select(item => 
                    UnwindMany(
                        item, 
                        getNext, 
                        isAfterLast)));
    }
    #endregion
}

它可以通过以下方式进行测试:

private static void Print<T>(IEnumerable<UnwoundItem<T>> items, Func<T, String> toString, Int32 level)
{
    var indent = new String(' ', level * 4);
    foreach (var item in items)
    {
        Console.Write(indent);
        Console.WriteLine(toString(item.Item));
        Print(item.UnwoundSubItems, toString, level + 1);
    }
}

var grandChild = new Tag { Name = "GrandChild", Id = 3 };
var grandChild2 = new Tag { Name = "GrandChild 2", Id = 33 };
var child = new Tag { Name = "Child", Id = 2, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 3, ChildTag = grandChild } } };
var child2 = new Tag { Name = "Child 2", Id = 22, Tagging = new List<Tagging> { new Tagging { ParentId = 2, ChildId = 33, ChildTag = grandChild2 } } };
var parent = new Tag { Name = "Parent", Id = 1,
    Tagging = new List<Tagging> {
        new Tagging { ParentId = 1, ChildId = 2, ChildTag = child },
        new Tagging { ParentId = 1, ChildId = 2, ChildTag = child2 } }
};
var fromParent = EnumerableExtensions
    .UnwindMany(
        parent,
        item =>
            item?.Tagging?.Select(tagging => tagging.ChildTag));
Console.WriteLine("Parent to child:");                
Print(new[] { fromParent }, item => item.Name, 0);