using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using Masuit.Tools.Systems; namespace Masuit.Tools.Models; /// /// 树形数据扩展 /// public static class TreeExtensions { /// /// 过滤 /// /// /// /// /// public static IEnumerable Filter(this IEnumerable items, Func func) where T : class, ITreeChildren { return Flatten(items).Where(func); } /// /// 过滤 /// /// /// /// /// public static IEnumerable> Filter(this IEnumerable> items, Func, bool> func) where T : class { return Flatten(items).Where(func); } /// /// 过滤 /// /// /// /// /// public static IEnumerable Filter(this T item, Func func) where T : class, ITreeChildren { return Flatten(item).Where(func); } /// /// 过滤 /// /// /// /// /// public static IEnumerable> Filter(this Tree item, Func, bool> func) where T : class { return Flatten(item).Where(func); } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this IEnumerable items, Action optionAction = null) where T : class, ITreeChildren { if (items == null) { yield break; } // 使用一个栈来存储待处理的节点 var stack = new Stack(); // 首先将所有项压入栈中 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(); foreach (var child in children) { optionAction?.Invoke(child, currentItem); stack.Push(child); // 将子节点压入栈中 } } } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this T p, Action optionAction = null) where T : class, ITreeChildren { if (p == null) { return []; } return Flatten([p], t => t.Children, optionAction); } /// /// 平铺开任意树形结构数据 /// /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this IEnumerable items, Func> selector, Action optionAction = null) { if (items == null) { yield break; } // 使用一个队列来存储待处理的节点 var queue = new Queue(); // 首先将所有项加入队列 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(); // 将所有子节点加入队列 foreach (var child in children) { // 执行平铺时的操作(如果有的话) optionAction?.Invoke(child, currentItem); queue.Enqueue(child); } } } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this IEnumerable> items, Action, Tree> optionAction = null) where T : class { if (items == null) { return []; } return Flatten(items, t => t.Children, optionAction); } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this Tree p, Action, Tree> optionAction = null) where T : class { if (p == null) { return []; } return Flatten([p], t => t.Children, optionAction); } /// /// 平铺开任意树形结构数据 /// /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this IEnumerable> items, Func, IEnumerable>> selector, Action, Tree> optionAction = null) { if (items == null) { yield break; } // 使用一个队列来存储待处理的节点 var queue = new Queue>(); // 首先将所有项加入队列 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>(); // 将所有子节点加入队列 foreach (var child in children) { // 执行平铺时的操作(如果有的话) optionAction?.Invoke(child, currentItem); queue.Enqueue(child); } } } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, string topValue = default) where T : ITreeParent, ITreeChildren { return ToTree(source, idSelector, pidSelector, topValue); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, int topValue = 0) where T : ITreeParent, ITreeChildren { return ToTree(source, idSelector, pidSelector, topValue); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, long topValue = 0) where T : ITreeParent, ITreeChildren { return ToTree(source, idSelector, pidSelector, topValue); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, Guid topValue = default) where T : ITreeParent, ITreeChildren { return ToTree(source, idSelector, pidSelector, topValue); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, TKey topValue = default) where T : ITreeParent, ITreeChildren where TKey : IComparable { if (source is IQueryable 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(); } /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : class, ITreeEntity { return ToTree(source); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// public static List ToTree(this IEnumerable source, TKey? topValue = null) where T : class, ITreeEntity where TKey : struct, IComparable { if (source is IQueryable queryable) { source = queryable.ToList(); } var list = source.Where(t => t != null).ToList(); return BuildTree(list, topValue).ToList(); } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// /// 根对象parentId的值 /// public static List ToTree(this IEnumerable source, Expression> idSelector, Expression> pidSelector, TKey? topValue = default) where T : ITreeChildren where TKey : struct { if (source is IQueryable 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(); } private static IEnumerable BuildTree(IEnumerable source, Func idSelector, Func pidSelector, TKey topValue = default) where T : ITreeChildren where TKey : IComparable { // 创建一个字典,用于快速查找节点的子节点 var childrenLookup = new Dictionary>(); var list = source as ICollection ?? source.ToList(); foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item)))) { childrenLookup[idSelector(item)] = new List(); } // 构建树结构 foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item)))) { childrenLookup[pidSelector(item)].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in list.Where(x => Equals(pidSelector(x), topValue))) { // 为根节点和所有子节点设置Children属性 // 使用队列来模拟递归过程 var queue = new Queue(); queue.Enqueue(root); while (queue.Count > 0) { // 出队当前节点 var current = queue.Dequeue(); // 为当前节点设置子节点 if (childrenLookup.TryGetValue(idSelector(current), out var children)) { current.Children = children; foreach (var child in children) { // 如果子节点实现了ITreeParent接口,则设置其Parent属性 if (child is ITreeParent tree) { tree.Parent = current; } // 将子节点入队以继续处理 queue.Enqueue(child); } } } yield return root; } } internal static IEnumerable BuildTree(IEnumerable source, TKey? topValue = default) where T : ITreeEntity where TKey : struct, IComparable { return BuildTree(source, t => t.Id, t => t.ParentId, topValue); } internal static IEnumerable BuildTree(IEnumerable source, string topValue = null) where T : ITreeEntity { return BuildTree(source, t => t.Id, t => t.ParentId, topValue); } internal static IEnumerable BuildTree(IEnumerable source, T parent) where T : ITreeEntity { // 创建一个字典,用于快速查找节点的子节点 var childrenLookup = new NullableDictionary>(); var list = source as ICollection ?? source.ToList(); foreach (var item in list.Where(item => !childrenLookup.ContainsKey(item.Id))) { childrenLookup[item.Id] = new List(); } // 构建树结构 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)) { // 为根节点和所有子节点设置Children属性 // 使用队列来模拟递归过程 var queue = new Queue(); queue.Enqueue(root); while (queue.Count > 0) { // 出队当前节点 var current = queue.Dequeue(); // 为当前节点设置子节点 if (childrenLookup.TryGetValue(current.Id, out var children)) { current.Children = children; foreach (var child in children) { // 如果子节点实现了ITreeParent接口,则设置其Parent属性 if (child is ITreeParent tree) { tree.Parent = current; } // 将子节点入队以继续处理 queue.Enqueue(child); } } } yield return root; } } private static IEnumerable BuildTree(IEnumerable source, Func idSelector, Func pidSelector, TKey? topValue = default) where T : ITreeChildren where TKey : struct { // 创建一个字典,用于快速查找节点的子节点 var childrenLookup = new NullableDictionary>(); var list = source as ICollection ?? source.ToList(); foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item)))) { childrenLookup[idSelector(item)] = new List(); } // 构建树结构 foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item) ?? default))) { childrenLookup[pidSelector(item) ?? default].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in list.Where(x => Equals(pidSelector(x), topValue))) { // 为根节点和所有子节点设置Children属性 // 使用队列来模拟递归过程 var queue = new Queue(); queue.Enqueue(root); while (queue.Count > 0) { // 出队当前节点 var current = queue.Dequeue(); // 为当前节点设置子节点 if (childrenLookup.TryGetValue(idSelector(current), out var children)) { current.Children = children; foreach (var child in children) { // 如果子节点实现了ITreeParent接口,则设置其Parent属性 if (child is ITreeParent tree) { tree.Parent = current; } // 将子节点入队以继续处理 queue.Enqueue(child); } } } yield return root; } } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// /// 根对象parentId的值 /// public static List> ToTreeGeneral(this IEnumerable source, Expression> idSelector, Expression> 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(); } private static IEnumerable> BuildTreeGeneral(List list, Func idSelector, Func pidSelector, TKey parent) where TKey : IComparable { // 创建一个字典,用于快速查找节点的子节点 var lookup = new Dictionary>>(); foreach (var item in list.Where(item => !lookup.ContainsKey(idSelector(item)))) { lookup[idSelector(item)] = new List>(); } // 构建树结构 foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && lookup.ContainsKey(pidSelector(item)))) { lookup[pidSelector(item)].Add(new Tree(item)); } // 找到根节点,即没有父节点的节点 foreach (var root in list.Where(x => Equals(pidSelector(x), parent))) { // 为根节点和所有子节点设置Children属性 var queue = new Queue>(); var item = new Tree(root); queue.Enqueue(item); while (queue.Count > 0) { // 出队当前节点 var current = queue.Dequeue(); // 为当前节点设置子节点 if (lookup.TryGetValue(idSelector(current.Value), out var children)) { current.Children = children; foreach (var child in children) { child.Parent = current.Parent; // 将子节点入队以继续处理 queue.Enqueue(child); } } } yield return item; } } /// /// 所有子级 /// public static ICollection AllChildren(this T tree) where T : ITreeChildren => GetChildren(tree, c => c.Children); /// /// 所有子级 /// public static ICollection AllChildren(this T tree, Func> selector) where T : ITreeChildren => GetChildren(tree, selector); /// /// 所有子级 /// public static ICollection> AllChildren(this Tree tree) => GetChildren(tree, c => c.Children); /// /// 所有子级 /// public static ICollection> AllChildren(this Tree tree, Func, IEnumerable>> selector) => GetChildren(tree, selector); /// /// 所有父级 /// public static List AllParent(this T tree) where T : class, ITreeParent => GetParents(tree, c => c.Parent); /// /// 所有父级 /// public static List AllParent(this T tree, Func selector) where T : class, ITreeParent => GetParents(tree, selector); /// /// 所有父级 /// public static List> AllParent(this Tree tree, Func, Tree> selector) => GetParents(tree, selector); /// /// 是否是根节点 /// public static bool IsRoot(this ITreeParent tree) where T : ITreeParent => tree.Parent == null; /// /// 是否是叶子节点 /// public static bool IsLeaf(this ITreeChildren tree) where T : ITreeChildren => tree.Children?.Count == 0; /// /// 是否是根节点 /// public static bool IsRoot(this Tree tree) => tree.Parent == null; /// /// 是否是叶子节点 /// public static bool IsLeaf(this Tree tree) => tree.Children?.Count == 0; /// /// 深度层级 /// public static int Level(this T tree) where T : class, ITreeParent { if (tree == null) { throw new ArgumentNullException(nameof(tree), "当前节点不能为null"); } // 使用一个队列来存储待处理的节点 var queue = new Queue(); queue.Enqueue(tree); int level = 1; // 循环直到队列为空 while (queue.Count > 0) { int nodeCount = queue.Count; // 当前层级的节点数 for (int i = 0; i < nodeCount; i++) { // 从队列中出队一个节点 T currentNode = queue.Dequeue(); // 如果当前节点是根节点,则返回当前层级 if (currentNode.Parent == null) { return level; } // 将当前节点的父节点入队 if (currentNode.Parent != null) { queue.Enqueue(currentNode.Parent); } } // 完成当前层级的遍历,准备进入下一层级 level++; } // 如果执行到这里,说明没有找到根节点 throw new InvalidOperationException("未找到根节点"); } /// /// 节点路径(UNIX路径格式,以“/”分隔) /// public static string Path(this T tree) where T : class, ITree => GetFullPath(tree, t => t.Name); /// /// 节点路径(UNIX路径格式,以“/”分隔) /// public static string Path(this T tree, Func selector) where T : class, ITreeParent => GetFullPath(tree, selector); /// /// 节点路径 /// /// /// 分隔符 public static string Path(this T tree, string separator) where T : class, ITree => GetFullPath(tree, t => t.Name, separator); /// /// 节点路径 /// /// /// /// 选择字段 /// 分隔符 /// public static string Path(this T tree, Func selector, string separator) where T : class, ITreeParent => GetFullPath(tree, selector, separator); /// /// 根节点 /// public static T Root(this T tree) where T : class, ITreeParent => GetRoot(tree, t => t.Parent); private static string GetFullPath(T c, Func selector, string separator = "/") where T : class, ITreeParent { if (c == null || selector == null) { throw new ArgumentNullException(c == null ? nameof(c) : nameof(selector), "当前节点或选择器不能为null"); } // 使用一个栈来存储节点,栈将逆序存储路径中的节点 var stack = new Stack(); var currentNode = c; while (currentNode != null) { stack.Push(currentNode); currentNode = currentNode.Parent; } // 构建路径字符串 return stack.Select(selector).Join(separator); } /// /// 根节点 /// public static T GetRoot(T c, Func selector) where T : class, ITreeParent { if (c == null || selector == null) { throw new ArgumentNullException(c == null ? nameof(c) : nameof(selector), "当前节点或父级选择器不能为null"); } // 使用一个集合来存储已访问的节点,以避免无限循环 var visited = new HashSet(); T currentNode = c; // 向上遍历直到找到根节点 while (currentNode != null) { // 如果当前节点已被访问,说明存在循环引用,抛出异常 if (!visited.Add(currentNode)) { throw new InvalidOperationException("节点存在循环引用"); } var parent = selector(currentNode); if (parent == null) { // 找到了根节点 return currentNode; } currentNode = parent; } // 如果currentNode为null,说明最初的节点c就是根节点 return c; } /// /// 递归取出所有下级 /// /// /// /// private static List GetChildren(T t, Func> selector) { if (t == null || selector == null) { return new List(); // 如果t或selector为null,则返回空列表 } var list = new List(); var queue = new Queue(); queue.Enqueue(t); while (queue.Count > 0) { var current = queue.Dequeue(); list.Add(current); // 将当前节点添加到结果列表 var children = selector(current) ?? new List(); foreach (var child in children) { // 只有当子节点还有自己的子节点时,才将其加入队列 if (selector(child).Any()) { queue.Enqueue(child); } } } return list; } /// /// 递归取出所有上级 /// /// /// /// private static List GetParents(T t, Func selector) where T : class { if (t == null || selector == null) { return new List(); // 如果t或selector为null,则返回空列表 } var parents = new List(); var current = t; while (current != null) { // 添加当前节点到父节点列表 parents.Add(current); // 通过selector函数获取当前节点的父节点 current = selector(current); // 如果selector返回了已经遍历过的节点,则停止遍历以避免无限循环 if (parents.Contains(current)) { break; } } // 由于是逆序添加的,所以需要反转列表 parents.Reverse(); return parents; } } public static class TreeExtensionLong { /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : class, ITreeEntity { return source.ToTree(); } } public static class TreeExtensionGuid { /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : class, ITreeEntity { return source.ToTree(); } } public static class TreeExtensionString { /// /// 平行集合转换成树形结构 /// /// /// /// /// public static List ToTree(this IEnumerable source, string topValue) where T : class, ITreeEntity { if (source is IQueryable queryable) { source = queryable.ToList(); } source = source.Where(t => t != null).ToList(); return TreeExtensions.BuildTree(source, topValue).ToList(); } /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : class, ITreeEntity { if (source is IQueryable 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(); } }