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)
的实际实现
这个怎么样...
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);