C#linq层次结构树

时间:2016-02-28 11:01:56

标签: c# linq recursion

标记类由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)

的实际实现

2 个答案:

答案 0 :(得分:3)

这个怎么样......

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))
...

答案 1 :(得分:1)

一个孩子只有一个父母。

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

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
}

以下列方式使用它们(我在Taggings中设置了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);