|
|
@@ -20,10 +20,8 @@ public static class TreeExtensions
|
|
|
/// <param name="items"></param>
|
|
|
/// <param name="func"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<T> Filter<T>(this IEnumerable<T> items, Func<T, bool> func) where T : class, ITreeChildren<T>
|
|
|
- {
|
|
|
- return Flatten(items).Where(func);
|
|
|
- }
|
|
|
+ public static IEnumerable<T> Filter<T>(this IEnumerable<T> items, Func<T, bool> func) where T : class, ITreeChildren<T>
|
|
|
+ => Flatten(items).Where(func);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 过滤
|
|
|
@@ -32,10 +30,8 @@ public static class TreeExtensions
|
|
|
/// <param name="items"></param>
|
|
|
/// <param name="func"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<Tree<T>> Filter<T>(this IEnumerable<Tree<T>> items, Func<Tree<T>, bool> func) where T : class
|
|
|
- {
|
|
|
- return Flatten(items).Where(func);
|
|
|
- }
|
|
|
+ public static IEnumerable<Tree<T>> Filter<T>(this IEnumerable<Tree<T>> items, Func<Tree<T>, bool> func) where T : class
|
|
|
+ => Flatten(items).Where(func);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 过滤
|
|
|
@@ -44,10 +40,8 @@ public static class TreeExtensions
|
|
|
/// <param name="item"></param>
|
|
|
/// <param name="func"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<T> Filter<T>(this T item, Func<T, bool> func) where T : class, ITreeChildren<T>
|
|
|
- {
|
|
|
- return Flatten(item).Where(func);
|
|
|
- }
|
|
|
+ public static IEnumerable<T> Filter<T>(this T item, Func<T, bool> func) where T : class, ITreeChildren<T>
|
|
|
+ => Flatten(item).Where(func);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 过滤
|
|
|
@@ -56,10 +50,8 @@ public static class TreeExtensions
|
|
|
/// <param name="item"></param>
|
|
|
/// <param name="func"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<Tree<T>> Filter<T>(this Tree<T> item, Func<Tree<T>, bool> func) where T : class
|
|
|
- {
|
|
|
- return Flatten(item).Where(func);
|
|
|
- }
|
|
|
+ public static IEnumerable<Tree<T>> Filter<T>(this Tree<T> item, Func<Tree<T>, bool> func) where T : class
|
|
|
+ => Flatten(item).Where(func);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平铺开
|
|
|
@@ -68,37 +60,9 @@ public static class TreeExtensions
|
|
|
/// <param name="items"></param>
|
|
|
/// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Action<T, T> optionAction = null) where T : class, ITreeChildren<T>
|
|
|
- {
|
|
|
- if (items == null)
|
|
|
- {
|
|
|
- yield break;
|
|
|
- }
|
|
|
-
|
|
|
- // 使用一个栈来存储待处理的节点
|
|
|
- var stack = new Stack<T>();
|
|
|
- // 首先将所有项压入栈中
|
|
|
- foreach (var item in items)
|
|
|
- {
|
|
|
- stack.Push(item);
|
|
|
- }
|
|
|
-
|
|
|
- // 循环直到栈为空
|
|
|
- while (stack.Count > 0)
|
|
|
- {
|
|
|
- // 弹出栈顶的节点
|
|
|
- var currentItem = stack.Pop();
|
|
|
- yield return currentItem;
|
|
|
-
|
|
|
- // 为当前节点设置子节点,如果optionAction不为空,则对每个子节点执行操作
|
|
|
- var children = currentItem.Children ?? new List<T>();
|
|
|
- foreach (var child in children)
|
|
|
- {
|
|
|
- optionAction?.Invoke(child, currentItem);
|
|
|
- stack.Push(child); // 将子节点压入栈中
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Action<T, T> optionAction = null)
|
|
|
+ where T : class, ITreeChildren<T>
|
|
|
+ => items.Flatten(i => i.Children ?? [], optionAction);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平铺开
|
|
|
@@ -108,37 +72,28 @@ public static class TreeExtensions
|
|
|
/// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
|
|
|
/// <returns></returns>
|
|
|
public static IEnumerable<T> Flatten<T>(this T p, Action<T, T> optionAction = null) where T : class, ITreeChildren<T>
|
|
|
- {
|
|
|
- if (p == null)
|
|
|
- {
|
|
|
- return [];
|
|
|
- }
|
|
|
-
|
|
|
- return Flatten([p], t => t.Children, optionAction);
|
|
|
- }
|
|
|
+ => p != null
|
|
|
+ ? Flatten([p], t => t.Children, optionAction)
|
|
|
+ : [];
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平铺开任意树形结构数据
|
|
|
/// </summary>
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
/// <param name="items"></param>
|
|
|
- /// <param name="selector"></param>
|
|
|
+ /// <param name="selector">子节点获取方法</param>
|
|
|
/// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
|
|
|
/// <returns></returns>
|
|
|
public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> selector, Action<T, T> optionAction = null)
|
|
|
{
|
|
|
if (items == null)
|
|
|
- {
|
|
|
yield break;
|
|
|
- }
|
|
|
|
|
|
// 使用一个队列来存储待处理的节点
|
|
|
var queue = new Queue<T>();
|
|
|
// 首先将所有项加入队列
|
|
|
foreach (var item in items)
|
|
|
- {
|
|
|
queue.Enqueue(item);
|
|
|
- }
|
|
|
|
|
|
// 循环直到队列为空
|
|
|
while (queue.Count > 0)
|
|
|
@@ -150,7 +105,7 @@ public static class TreeExtensions
|
|
|
yield return currentItem;
|
|
|
|
|
|
// 获取当前节点的所有子节点
|
|
|
- var children = selector(currentItem) ?? new List<T>();
|
|
|
+ var children = selector(currentItem) ?? [];
|
|
|
|
|
|
// 将所有子节点加入队列
|
|
|
foreach (var child in children)
|
|
|
@@ -169,15 +124,11 @@ public static class TreeExtensions
|
|
|
/// <param name="items"></param>
|
|
|
/// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
|
|
|
/// <returns></returns>
|
|
|
- public static IEnumerable<Tree<T>> Flatten<T>(this IEnumerable<Tree<T>> items, Action<Tree<T>, Tree<T>> optionAction = null) where T : class
|
|
|
- {
|
|
|
- if (items == null)
|
|
|
- {
|
|
|
- return [];
|
|
|
- }
|
|
|
-
|
|
|
- return Flatten(items, t => t.Children, optionAction);
|
|
|
- }
|
|
|
+ public static IEnumerable<Tree<T>> Flatten<T>(this IEnumerable<Tree<T>> items,
|
|
|
+ Action<Tree<T>, Tree<T>> optionAction = null) where T : class
|
|
|
+ => items != null
|
|
|
+ ? Flatten(items, t => t.Children, optionAction)
|
|
|
+ : [];
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平铺开
|
|
|
@@ -196,51 +147,6 @@ public static class TreeExtensions
|
|
|
return Flatten([p], t => t.Children, optionAction);
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// 平铺开任意树形结构数据
|
|
|
- /// </summary>
|
|
|
- /// <typeparam name="T"></typeparam>
|
|
|
- /// <param name="items"></param>
|
|
|
- /// <param name="selector"></param>
|
|
|
- /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
|
|
|
- /// <returns></returns>
|
|
|
- public static IEnumerable<Tree<T>> Flatten<T>(this IEnumerable<Tree<T>> items, Func<Tree<T>, IEnumerable<Tree<T>>> selector, Action<Tree<T>, Tree<T>> optionAction = null)
|
|
|
- {
|
|
|
- if (items == null)
|
|
|
- {
|
|
|
- yield break;
|
|
|
- }
|
|
|
-
|
|
|
- // 使用一个队列来存储待处理的节点
|
|
|
- var queue = new Queue<Tree<T>>();
|
|
|
- // 首先将所有项加入队列
|
|
|
- foreach (var item in items)
|
|
|
- {
|
|
|
- queue.Enqueue(item);
|
|
|
- }
|
|
|
-
|
|
|
- // 循环直到队列为空
|
|
|
- while (queue.Count > 0)
|
|
|
- {
|
|
|
- // 从队列中取出当前节点
|
|
|
- var currentItem = queue.Dequeue();
|
|
|
-
|
|
|
- // 将当前节点返回
|
|
|
- yield return currentItem;
|
|
|
-
|
|
|
- // 获取当前节点的所有子节点
|
|
|
- var children = selector(currentItem) ?? new List<Tree<T>>();
|
|
|
-
|
|
|
- // 将所有子节点加入队列
|
|
|
- foreach (var child in children)
|
|
|
- {
|
|
|
- // 执行平铺时的操作(如果有的话)
|
|
|
- optionAction?.Invoke(child, currentItem);
|
|
|
- queue.Enqueue(child);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
/// </summary>
|
|
|
@@ -250,10 +156,8 @@ public static class TreeExtensions
|
|
|
/// <param name="pidSelector"></param>
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, string>> idSelector, Expression<Func<T, string>> pidSelector, string topValue = default) where T : ITreeParent<T>, ITreeChildren<T>
|
|
|
- {
|
|
|
- return ToTree<T, string>(source, idSelector, pidSelector, topValue);
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, string>> idSelector, Expression<Func<T, string>> pidSelector, string topValue = null) where T : ITreeParent<T>, ITreeChildren<T>
|
|
|
+ => ToTree<T, string>(source, idSelector, pidSelector, topValue);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -265,9 +169,7 @@ public static class TreeExtensions
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, int>> idSelector, Expression<Func<T, int>> pidSelector, int topValue = 0) where T : ITreeParent<T>, ITreeChildren<T>
|
|
|
- {
|
|
|
- return ToTree<T, int>(source, idSelector, pidSelector, topValue);
|
|
|
- }
|
|
|
+ => ToTree<T, int>(source, idSelector, pidSelector, topValue);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -279,9 +181,7 @@ public static class TreeExtensions
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, long>> idSelector, Expression<Func<T, long>> pidSelector, long topValue = 0) where T : ITreeParent<T>, ITreeChildren<T>
|
|
|
- {
|
|
|
- return ToTree<T, long>(source, idSelector, pidSelector, topValue);
|
|
|
- }
|
|
|
+ =>ToTree<T, long>(source, idSelector, pidSelector, topValue);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -293,9 +193,7 @@ public static class TreeExtensions
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T>(this IEnumerable<T> source, Expression<Func<T, Guid>> idSelector, Expression<Func<T, Guid>> pidSelector, Guid topValue = default) where T : ITreeParent<T>, ITreeChildren<T>
|
|
|
- {
|
|
|
- return ToTree<T, Guid>(source, idSelector, pidSelector, topValue);
|
|
|
- }
|
|
|
+ =>ToTree<T, Guid>(source, idSelector, pidSelector, topValue);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -308,21 +206,9 @@ public static class TreeExtensions
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey>> pidSelector, TKey topValue = default) where T : ITreeParent<T>, ITreeChildren<T> where TKey : IComparable
|
|
|
- {
|
|
|
- if (source is IQueryable<T> queryable)
|
|
|
- {
|
|
|
- source = queryable.ToList();
|
|
|
- }
|
|
|
-
|
|
|
- if (idSelector.Body.ToString() == pidSelector.Body.ToString())
|
|
|
- {
|
|
|
- throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
- }
|
|
|
-
|
|
|
- var pidFunc = pidSelector.Compile();
|
|
|
- var idFunc = idSelector.Compile();
|
|
|
- return BuildTree(source.Where(t => t != null), idFunc, pidFunc, topValue).ToList();
|
|
|
- }
|
|
|
+ => idSelector.Body.ToString() != pidSelector.Body.ToString()
|
|
|
+ ? BuildTree(source.Enumerable2NonNullList(), idSelector.Compile(), pidSelector.Compile(), topValue).ToList()
|
|
|
+ : throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -331,9 +217,7 @@ public static class TreeExtensions
|
|
|
/// <param name="source"></param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, int>
|
|
|
- {
|
|
|
- return ToTree<T, int>(source);
|
|
|
- }
|
|
|
+ =>ToTree<T, int>(source);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -343,16 +227,8 @@ public static class TreeExtensions
|
|
|
/// <param name="source"></param>
|
|
|
/// <param name="topValue"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, TKey? topValue = null) where T : class, ITreeEntity<T, TKey> where TKey : struct, IComparable
|
|
|
- {
|
|
|
- if (source is IQueryable<T> queryable)
|
|
|
- {
|
|
|
- source = queryable.ToList();
|
|
|
- }
|
|
|
-
|
|
|
- var list = source.Where(t => t != null).ToList();
|
|
|
- return BuildTree(list, topValue).ToList();
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, TKey? topValue = null) where T : class, ITreeEntity<T, TKey> where TKey : struct, IComparable
|
|
|
+ => BuildTree(source.Enumerable2NonNullList(), topValue).ToList();
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -364,23 +240,10 @@ public static class TreeExtensions
|
|
|
/// <param name="pidSelector"></param>
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey?>> pidSelector, TKey? topValue = default) where T : ITreeChildren<T> where TKey : struct
|
|
|
- {
|
|
|
- if (source is IQueryable<T> queryable)
|
|
|
- {
|
|
|
- source = queryable.ToList();
|
|
|
- }
|
|
|
-
|
|
|
- if (idSelector.Body.ToString() == pidSelector.Body.ToString())
|
|
|
- {
|
|
|
- throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
- }
|
|
|
-
|
|
|
- var pidFunc = pidSelector.Compile();
|
|
|
- var idFunc = idSelector.Compile();
|
|
|
- source = source.Where(t => t != null).ToList();
|
|
|
- return BuildTree(source, idFunc, pidFunc, topValue).ToList();
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey?>> pidSelector, TKey? topValue = null) where T : ITreeChildren<T> where TKey : struct
|
|
|
+ => idSelector.Body.ToString() != pidSelector.Body.ToString()
|
|
|
+ ? BuildTree(source.Enumerable2NonNullList(), idSelector.Compile(), pidSelector.Compile(), topValue).ToList()
|
|
|
+ : throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
|
|
|
private static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, Func<T, TKey> idSelector, Func<T, TKey> pidSelector, TKey topValue = default) where T : ITreeChildren<T> where TKey : IComparable
|
|
|
{
|
|
|
@@ -389,7 +252,7 @@ public static class TreeExtensions
|
|
|
var list = source as ICollection<T> ?? source.ToList();
|
|
|
foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
|
|
|
{
|
|
|
- childrenLookup[idSelector(item)] = new List<T>();
|
|
|
+ childrenLookup[idSelector(item)] = [];
|
|
|
}
|
|
|
|
|
|
// 构建树结构
|
|
|
@@ -432,15 +295,11 @@ public static class TreeExtensions
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- internal static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, TKey? topValue = default) where T : ITreeEntity<T, TKey> where TKey : struct, IComparable
|
|
|
- {
|
|
|
- return BuildTree(source, t => t.Id, t => t.ParentId, topValue);
|
|
|
- }
|
|
|
+ internal static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, TKey? topValue = null) where T : ITreeEntity<T, TKey> where TKey : struct, IComparable
|
|
|
+ => BuildTree(source, t => t.Id, t => t.ParentId, topValue);
|
|
|
|
|
|
- internal static IEnumerable<T> BuildTree<T>(IEnumerable<T> source, string topValue = null) where T : ITreeEntity<T>
|
|
|
- {
|
|
|
- return BuildTree(source, t => t.Id, t => t.ParentId, topValue);
|
|
|
- }
|
|
|
+ internal static IEnumerable<T> BuildTree<T>(IEnumerable<T> source, string topValue = null) where T : ITreeEntity<T>
|
|
|
+ => BuildTree(source, t => t.Id, t => t.ParentId, topValue);
|
|
|
|
|
|
internal static IEnumerable<T> BuildTree<T>(IEnumerable<T> source, T parent) where T : ITreeEntity<T>
|
|
|
{
|
|
|
@@ -448,15 +307,11 @@ public static class TreeExtensions
|
|
|
var childrenLookup = new NullableDictionary<string, List<T>>();
|
|
|
var list = source as ICollection<T> ?? source.ToList();
|
|
|
foreach (var item in list.Where(item => !childrenLookup.ContainsKey(item.Id)))
|
|
|
- {
|
|
|
- childrenLookup[item.Id] = new List<T>();
|
|
|
- }
|
|
|
+ childrenLookup[item.Id] = [];
|
|
|
|
|
|
// 构建树结构
|
|
|
foreach (var item in list.Where(item => !string.IsNullOrEmpty(item.ParentId) && childrenLookup.ContainsKey(item.ParentId)))
|
|
|
- {
|
|
|
childrenLookup[item.ParentId].Add(item);
|
|
|
- }
|
|
|
|
|
|
// 找到根节点,即没有父节点的节点
|
|
|
foreach (var root in list.Where(x => x.Id == parent.Id))
|
|
|
@@ -478,9 +333,7 @@ public static class TreeExtensions
|
|
|
{
|
|
|
// 如果子节点实现了ITreeParent接口,则设置其Parent属性
|
|
|
if (child is ITreeParent<T> tree)
|
|
|
- {
|
|
|
tree.Parent = current;
|
|
|
- }
|
|
|
|
|
|
// 将子节点入队以继续处理
|
|
|
queue.Enqueue(child);
|
|
|
@@ -492,14 +345,14 @@ public static class TreeExtensions
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- private static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, Func<T, TKey> idSelector, Func<T, TKey?> pidSelector, TKey? topValue = default) where T : ITreeChildren<T> where TKey : struct
|
|
|
+ private static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, Func<T, TKey> idSelector, Func<T, TKey?> pidSelector, TKey? topValue = null) where T : ITreeChildren<T> where TKey : struct
|
|
|
{
|
|
|
// 创建一个字典,用于快速查找节点的子节点
|
|
|
var childrenLookup = new NullableDictionary<TKey, List<T>>();
|
|
|
var list = source as ICollection<T> ?? source.ToList();
|
|
|
foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
|
|
|
{
|
|
|
- childrenLookup[idSelector(item)] = new List<T>();
|
|
|
+ childrenLookup[idSelector(item)] = [];
|
|
|
}
|
|
|
|
|
|
// 构建树结构
|
|
|
@@ -553,17 +406,13 @@ public static class TreeExtensions
|
|
|
/// <param name="topValue">根对象parentId的值</param>
|
|
|
/// <returns></returns>
|
|
|
public static List<Tree<T>> ToTreeGeneral<T, TKey>(this IEnumerable<T> source, Expression<Func<T, TKey>> idSelector, Expression<Func<T, TKey>> pidSelector, TKey topValue = default) where TKey : IComparable
|
|
|
- {
|
|
|
- if (idSelector.Body.ToString() == pidSelector.Body.ToString())
|
|
|
- {
|
|
|
- throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
- }
|
|
|
-
|
|
|
- var pidFunc = pidSelector.Compile();
|
|
|
- var idFunc = idSelector.Compile();
|
|
|
- var list = source.Where(t => t != null).ToList();
|
|
|
- return BuildTreeGeneral(list, idFunc, pidFunc, topValue).ToList();
|
|
|
- }
|
|
|
+ => idSelector.Body.ToString() != pidSelector.Body.ToString()
|
|
|
+ ? BuildTreeGeneral(
|
|
|
+ source.Where(t => t != null).ToList(),
|
|
|
+ idSelector.Compile(),
|
|
|
+ pidSelector.Compile(),
|
|
|
+ topValue).ToList()
|
|
|
+ : throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
|
|
|
|
|
|
private static IEnumerable<Tree<T>> BuildTreeGeneral<T, TKey>(List<T> list, Func<T, TKey> idSelector, Func<T, TKey> pidSelector, TKey parent) where TKey : IComparable
|
|
|
{
|
|
|
@@ -571,7 +420,7 @@ public static class TreeExtensions
|
|
|
var lookup = new Dictionary<TKey, List<Tree<T>>>();
|
|
|
foreach (var item in list.Where(item => !lookup.ContainsKey(idSelector(item))))
|
|
|
{
|
|
|
- lookup[idSelector(item)] = new List<Tree<T>>();
|
|
|
+ lookup[idSelector(item)] = [];
|
|
|
}
|
|
|
|
|
|
// 构建树结构
|
|
|
@@ -612,57 +461,68 @@ public static class TreeExtensions
|
|
|
/// <summary>
|
|
|
/// 所有子级
|
|
|
/// </summary>
|
|
|
- public static ICollection<T> AllChildren<T>(this T tree) where T : ITreeChildren<T> => GetChildren(tree, c => c.Children);
|
|
|
+ public static ICollection<T> AllChildren<T>(this T tree) where T : ITreeChildren<T>
|
|
|
+ => GetChildren(tree, c => c.Children);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有子级
|
|
|
/// </summary>
|
|
|
- public static ICollection<T> AllChildren<T>(this T tree, Func<T, IEnumerable<T>> selector) where T : ITreeChildren<T> => GetChildren(tree, selector);
|
|
|
+ public static ICollection<T> AllChildren<T>(this T tree, Func<T, IEnumerable<T>> selector) where T : ITreeChildren<T>
|
|
|
+ => GetChildren(tree, selector);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有子级
|
|
|
/// </summary>
|
|
|
- public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree) => GetChildren(tree, c => c.Children);
|
|
|
+ public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree)
|
|
|
+ => GetChildren(tree, c => c.Children);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有子级
|
|
|
/// </summary>
|
|
|
- public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree, Func<Tree<T>, IEnumerable<Tree<T>>> selector) => GetChildren(tree, selector);
|
|
|
+ public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree, Func<Tree<T>, IEnumerable<Tree<T>>> selector)
|
|
|
+ => GetChildren(tree, selector);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有父级
|
|
|
/// </summary>
|
|
|
- public static List<T> AllParent<T>(this T tree) where T : class, ITreeParent<T> => GetParents(tree, c => c.Parent);
|
|
|
+ public static List<T> AllParent<T>(this T tree) where T : class, ITreeParent<T>
|
|
|
+ => GetParents(tree, c => c.Parent);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有父级
|
|
|
/// </summary>
|
|
|
- public static List<T> AllParent<T>(this T tree, Func<T, T> selector) where T : class, ITreeParent<T> => GetParents(tree, selector);
|
|
|
+ public static List<T> AllParent<T>(this T tree, Func<T, T> selector) where T : class, ITreeParent<T>
|
|
|
+ => GetParents(tree, selector);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 所有父级
|
|
|
/// </summary>
|
|
|
- public static List<Tree<T>> AllParent<T>(this Tree<T> tree, Func<Tree<T>, Tree<T>> selector) => GetParents(tree, selector);
|
|
|
+ public static List<Tree<T>> AllParent<T>(this Tree<T> tree, Func<Tree<T>, Tree<T>> selector)
|
|
|
+ => GetParents(tree, selector);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是否是根节点
|
|
|
/// </summary>
|
|
|
- public static bool IsRoot<T>(this ITreeParent<T> tree) where T : ITreeParent<T> => tree.Parent == null;
|
|
|
+ public static bool IsRoot<T>(this ITreeParent<T> tree) where T : ITreeParent<T>
|
|
|
+ => tree.Parent == null;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是否是叶子节点
|
|
|
/// </summary>
|
|
|
- public static bool IsLeaf<T>(this ITreeChildren<T> tree) where T : ITreeChildren<T> => tree.Children?.Count == 0;
|
|
|
+ public static bool IsLeaf<T>(this ITreeChildren<T> tree) where T : ITreeChildren<T>
|
|
|
+ => tree.Children?.Count == 0;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是否是根节点
|
|
|
/// </summary>
|
|
|
- public static bool IsRoot<T>(this Tree<T> tree) => tree.Parent == null;
|
|
|
+ public static bool IsRoot<T>(this Tree<T> tree)
|
|
|
+ => tree.Parent == null;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 是否是叶子节点
|
|
|
/// </summary>
|
|
|
- public static bool IsLeaf<T>(this Tree<T> tree) => tree.Children?.Count == 0;
|
|
|
+ public static bool IsLeaf<T>(this Tree<T> tree)
|
|
|
+ => tree.Children?.Count == 0;
|
|
|
|
|
|
/// <summary>
|
|
|
/// 深度层级
|
|
|
@@ -670,9 +530,7 @@ public static class TreeExtensions
|
|
|
public static int Level<T>(this T tree) where T : class, ITreeParent<T>
|
|
|
{
|
|
|
if (tree == null)
|
|
|
- {
|
|
|
throw new ArgumentNullException(nameof(tree), "当前节点不能为null");
|
|
|
- }
|
|
|
|
|
|
// 使用一个队列来存储待处理的节点
|
|
|
var queue = new Queue<T>();
|
|
|
@@ -691,15 +549,11 @@ public static class TreeExtensions
|
|
|
|
|
|
// 如果当前节点是根节点,则返回当前层级
|
|
|
if (currentNode.Parent == null)
|
|
|
- {
|
|
|
return level;
|
|
|
- }
|
|
|
|
|
|
// 将当前节点的父节点入队
|
|
|
if (currentNode.Parent != null)
|
|
|
- {
|
|
|
queue.Enqueue(currentNode.Parent);
|
|
|
- }
|
|
|
}
|
|
|
|
|
|
// 完成当前层级的遍历,准备进入下一层级
|
|
|
@@ -713,12 +567,14 @@ public static class TreeExtensions
|
|
|
/// <summary>
|
|
|
/// 节点路径(UNIX路径格式,以“/”分隔)
|
|
|
/// </summary>
|
|
|
- public static string Path<T>(this T tree) where T : class, ITree<T> => GetFullPath(tree, t => t.Name);
|
|
|
+ public static string Path<T>(this T tree) where T : class, ITree<T>
|
|
|
+ => GetFullPath(tree, t => t.Name);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 节点路径(UNIX路径格式,以“/”分隔)
|
|
|
/// </summary>
|
|
|
- public static string Path<T>(this T tree, Func<T, string> selector) where T : class, ITreeParent<T> => GetFullPath(tree, selector);
|
|
|
+ public static string Path<T>(this T tree, Func<T, string> selector) where T : class, ITreeParent<T>
|
|
|
+ => GetFullPath(tree, selector);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 节点路径
|
|
|
@@ -735,12 +591,14 @@ public static class TreeExtensions
|
|
|
/// <param name="selector">选择字段</param>
|
|
|
/// <param name="separator">分隔符</param>
|
|
|
/// <returns></returns>
|
|
|
- public static string Path<T>(this T tree, Func<T, string> selector, string separator) where T : class, ITreeParent<T> => GetFullPath(tree, selector, separator);
|
|
|
+ public static string Path<T>(this T tree, Func<T, string> selector, string separator) where T : class, ITreeParent<T>
|
|
|
+ => GetFullPath(tree, selector, separator);
|
|
|
|
|
|
/// <summary>
|
|
|
/// 根节点
|
|
|
/// </summary>
|
|
|
- public static T Root<T>(this T tree) where T : class, ITreeParent<T> => GetRoot(tree, t => t.Parent);
|
|
|
+ public static T Root<T>(this T tree) where T : class, ITreeParent<T>
|
|
|
+ => GetRoot(tree, t => t.Parent);
|
|
|
|
|
|
private static string GetFullPath<T>(T c, Func<T, string> selector, string separator = "/") where T : class, ITreeParent<T>
|
|
|
{
|
|
|
@@ -774,29 +632,21 @@ public static class TreeExtensions
|
|
|
|
|
|
// 使用一个集合来存储已访问的节点,以避免无限循环
|
|
|
var visited = new HashSet<T>();
|
|
|
- T currentNode = c;
|
|
|
+ var currentNode = c;
|
|
|
|
|
|
// 向上遍历直到找到根节点
|
|
|
- while (currentNode != null)
|
|
|
+ while (true)
|
|
|
{
|
|
|
// 如果当前节点已被访问,说明存在循环引用,抛出异常
|
|
|
if (!visited.Add(currentNode))
|
|
|
- {
|
|
|
throw new InvalidOperationException("节点存在循环引用");
|
|
|
- }
|
|
|
|
|
|
var parent = selector(currentNode);
|
|
|
- if (parent == null)
|
|
|
- {
|
|
|
- // 找到了根节点
|
|
|
- return currentNode;
|
|
|
- }
|
|
|
-
|
|
|
- currentNode = parent;
|
|
|
+ if (parent != null)
|
|
|
+ currentNode = parent;
|
|
|
+ else
|
|
|
+ return currentNode;// 找到了根节点
|
|
|
}
|
|
|
-
|
|
|
- // 如果currentNode为null,说明最初的节点c就是根节点
|
|
|
- return c;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -808,9 +658,7 @@ public static class TreeExtensions
|
|
|
private static List<T> GetChildren<T>(T t, Func<T, IEnumerable<T>> selector)
|
|
|
{
|
|
|
if (t == null || selector == null)
|
|
|
- {
|
|
|
- return new List<T>(); // 如果t或selector为null,则返回空列表
|
|
|
- }
|
|
|
+ return []; // 如果t或selector为null,则返回空列表
|
|
|
|
|
|
var list = new List<T>();
|
|
|
var queue = new Queue<T>();
|
|
|
@@ -842,9 +690,7 @@ public static class TreeExtensions
|
|
|
private static List<T> GetParents<T>(T t, Func<T, T> selector) where T : class
|
|
|
{
|
|
|
if (t == null || selector == null)
|
|
|
- {
|
|
|
- return new List<T>(); // 如果t或selector为null,则返回空列表
|
|
|
- }
|
|
|
+ return []; // 如果t或selector为null,则返回空列表
|
|
|
|
|
|
var parents = new List<T>();
|
|
|
var current = t;
|
|
|
@@ -880,10 +726,8 @@ public static class TreeExtensionLong
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
/// <param name="source"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, long>
|
|
|
- {
|
|
|
- return source.ToTree<T, long>();
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, long>
|
|
|
+ => source.ToTree<T, long>();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -897,10 +741,8 @@ public static class TreeExtensionGuid
|
|
|
/// <typeparam name="T"></typeparam>
|
|
|
/// <param name="source"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, Guid>
|
|
|
- {
|
|
|
- return source.ToTree<T, Guid>();
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, Guid>
|
|
|
+ => source.ToTree<T, Guid>();
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
@@ -915,16 +757,8 @@ public static class TreeExtensionString
|
|
|
/// <param name="source"></param>
|
|
|
/// <param name="topValue"></param>
|
|
|
/// <returns></returns>
|
|
|
- public static List<T> ToTree<T>(this IEnumerable<T> source, string topValue) where T : class, ITreeEntity<T>
|
|
|
- {
|
|
|
- if (source is IQueryable<T> queryable)
|
|
|
- {
|
|
|
- source = queryable.ToList();
|
|
|
- }
|
|
|
-
|
|
|
- source = source.Where(t => t != null).ToList();
|
|
|
- return TreeExtensions.BuildTree(source, topValue).ToList();
|
|
|
- }
|
|
|
+ public static List<T> ToTree<T>(this IEnumerable<T> source, string topValue) where T : class, ITreeEntity<T>
|
|
|
+ => TreeExtensions.BuildTree(source.Enumerable2NonNullList(), topValue).ToList();
|
|
|
|
|
|
/// <summary>
|
|
|
/// 平行集合转换成树形结构
|
|
|
@@ -933,13 +767,26 @@ public static class TreeExtensionString
|
|
|
/// <param name="source"></param>
|
|
|
/// <returns></returns>
|
|
|
public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T>
|
|
|
- {
|
|
|
- if (source is IQueryable<T> queryable)
|
|
|
- {
|
|
|
- source = queryable.ToList();
|
|
|
- }
|
|
|
-
|
|
|
- source = source.Where(t => t != null).ToList();
|
|
|
- return source.Where(t => string.IsNullOrEmpty(t.ParentId)).SelectMany(parent => TreeExtensions.BuildTree(source, parent)).ToList();
|
|
|
+ => source.Enumerable2NonNullList()
|
|
|
+ .Where(t => string.IsNullOrEmpty(t.ParentId))
|
|
|
+ .SelectMany(parent => TreeExtensions.BuildTree(source, parent)).ToList();
|
|
|
+}
|
|
|
+file static class TreeExtensionCommon
|
|
|
+{
|
|
|
+ /// <summary>
|
|
|
+ /// 将IEnumerable转换成不含null的List,若T为IQueryable,则将数据读取到内存
|
|
|
+ /// </summary>
|
|
|
+ /// <typeparam name="T"></typeparam>
|
|
|
+ /// <param name="source"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ public static List<T> Enumerable2NonNullList<T>(this IEnumerable<T> source)
|
|
|
+ {
|
|
|
+ source = source is IQueryable<T> queryable
|
|
|
+ ? queryable.AsEnumerable()
|
|
|
+ : source;
|
|
|
+ return (typeof(T).IsValueType
|
|
|
+ ? source
|
|
|
+ : source.Where(t => t != null))
|
|
|
+ .ToList();
|
|
|
}
|
|
|
}
|