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 { foreach (var item in items.Where(i => i != null)) { item.Children ??= new List(); item.Children = item.Children.Filter(func).ToList(); if (item.Children.Any() || func(item)) { yield return item; } } } /// /// 过滤 /// /// /// /// /// public static IEnumerable> Filter(this IEnumerable> items, Func, bool> func) where T : class { foreach (var item in items.Where(i => i != null)) { item.Children ??= new List>(); item.Children = item.Children.Filter(func).ToList(); if (item.Children.Any() || func(item)) { yield return item; } } } /// /// 过滤 /// /// /// /// /// public static IEnumerable Filter(this T item, Func func) where T : class, ITreeChildren { return new[] { item }.Filter(func); } /// /// 过滤 /// /// /// /// /// public static IEnumerable> Filter(this Tree item, Func, bool> func) where T : class { return new[] { item }.Filter(func); } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this IEnumerable items, Action optionAction = null) where T : class, ITreeChildren { foreach (var item in items) { yield return item; item.Children ??= new List(); item.Children.ForEach(c => optionAction?.Invoke(c, item)); foreach (var children in item.Children.Flatten(optionAction)) { yield return children; } } } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this T p, Action optionAction = null) where T : class, ITreeChildren { yield return p; foreach (var item in p.Children) { yield return item; item.Children ??= new List(); item.Children.ForEach(c => optionAction?.Invoke(c, item)); foreach (var children in item.Children.Flatten()) { yield return children; } } } /// /// 平铺开任意树形结构数据 /// /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable Flatten(this IEnumerable items, Func> selector, Action optionAction = null) { foreach (var item in items) { yield return item; selector(item).ForEach(c => optionAction?.Invoke(c, item)); foreach (var children in selector(item).Flatten(selector)) { yield return children; } } } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this IEnumerable> items, Action, Tree> optionAction = null) where T : class { foreach (var item in items) { yield return item; item.Children ??= new List>(); item.Children.ForEach(c => optionAction?.Invoke(c, item)); foreach (var tree in item.Children.Flatten()) { yield return tree; } } } /// /// 平铺开 /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this Tree p, Action, Tree> optionAction = null) where T : class { yield return p; foreach (var item in p.Children) { yield return item; item.Children ??= new List>(); item.Children.ForEach(c => optionAction?.Invoke(c, item)); foreach (var tree in item.Children.Flatten()) { yield return tree; } } } /// /// 平铺开任意树形结构数据 /// /// /// /// /// 平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象 /// public static IEnumerable> Flatten(this IEnumerable> items, Func, IEnumerable>> selector, Action, Tree> optionAction = null) { foreach (var item in items) { yield return item; item.Children.ForEach(c => optionAction?.Invoke(c, item)); foreach (var tree in selector(item).Flatten(selector)) { yield return tree; } } } /// /// 平行集合转换成树形结构 /// /// /// /// /// /// 根对象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 : ITreeEntity { return ToTree(source); } /// /// 平行集合转换成树形结构 /// /// /// /// /// public static List ToTree(this IEnumerable source, TKey? topValue = default) where T : ITreeEntity where TKey : struct, IComparable { if (source is IQueryable queryable) { source = queryable.ToList(); } source = source.Where(t => t != null).ToList(); return BuildTree(source, 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(); } 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>(); foreach (var item in source.Where(item => !childrenLookup.ContainsKey(idSelector(item)))) { childrenLookup[idSelector(item)] = new List(); } // 构建树结构 foreach (var item in source.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item)))) { childrenLookup[pidSelector(item)].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in source.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 { // 创建一个字典,用于快速查找节点的子节点 var childrenLookup = new NullableDictionary>(); foreach (var item in source.Where(item => !childrenLookup.ContainsKey(item.Id))) { childrenLookup[item.Id] = new List(); } // 构建树结构 foreach (var item in source.Where(item => (item.ParentId != null || !Equals(item.ParentId, default(TKey))) && childrenLookup.ContainsKey(item.ParentId ?? default))) { childrenLookup[item.ParentId ?? default].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in source.Where(x => Equals(x.ParentId, topValue))) { // 为根节点和所有子节点设置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; } } internal static IEnumerable BuildTree(IEnumerable source, string topValue = null) where T : ITreeEntity { // 创建一个字典,用于快速查找节点的子节点 var childrenLookup = new NullableDictionary>(); foreach (var item in source.Where(item => !childrenLookup.ContainsKey(item.Id))) { childrenLookup[item.Id] = new List(); } // 构建树结构 foreach (var item in source.Where(item => !string.IsNullOrEmpty(item.ParentId) && childrenLookup.ContainsKey(item.ParentId))) { childrenLookup[item.ParentId].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in source.Where(x => Equals(x.ParentId, topValue))) { // 为根节点和所有子节点设置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>(); foreach (var item in source.Where(item => !childrenLookup.ContainsKey(idSelector(item)))) { childrenLookup[idSelector(item)] = new List(); } // 构建树结构 foreach (var item in source.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item) ?? default))) { childrenLookup[pidSelector(item) ?? default].Add(item); } // 找到根节点,即没有父节点的节点 foreach (var root in source.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(); source = source.Where(t => t != null); var temp = new List>(); foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue))) { var parent = new Tree(item); BuildTree(source, parent, idFunc, pidFunc); temp.Add(parent); } return temp; } private static void BuildTree(IEnumerable source, Tree parent, Func idSelector, Func pidSelector) where TKey : IComparable { var temp = new List>(); foreach (var item in source.Where(item => pidSelector(item)?.Equals(idSelector(parent.Value)) == true)) { var p = new Tree(item); BuildTree(source, p, idSelector, pidSelector); p.Parent = parent.Value; temp.Add(p); } parent.Children = temp; } /// /// 所有子级 /// 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 ICollection AllParent(this T tree) where T : ITreeParent => GetParents(tree, c => c.Parent); /// /// 所有父级 /// public static ICollection AllParent(this T tree, Func selector) where T : ITreeParent => GetParents(tree, selector); /// /// 所有父级 /// public static ICollection> 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 ITreeParent tree) where T : ITreeParent => IsRoot(tree) ? 1 : Level(tree.Parent) + 1; /// /// 节点路径(UNIX路径格式,以“/”分隔) /// public static string Path(this T tree) where T : ITree => GetFullPath(tree, t => t.Name); /// /// 节点路径(UNIX路径格式,以“/”分隔) /// public static string Path(this T tree, Func selector) where T : ITreeParent => GetFullPath(tree, selector); /// /// 节点路径 /// /// /// 分隔符 public static string Path(this T tree, string separator) where T : ITree => GetFullPath(tree, t => t.Name, separator); /// /// 节点路径 /// /// /// /// 选择字段 /// 分隔符 /// public static string Path(this T tree, Func selector, string separator) where T : ITreeParent => GetFullPath(tree, selector, separator); /// /// 根节点 /// public static T Root(this T tree) where T : ITreeParent => GetRoot(tree, t => t.Parent); private static string GetFullPath(T c, Func selector, string separator = "/") where T : ITreeParent => c.Parent != null ? GetFullPath(c.Parent, selector, separator) + separator + selector(c) : selector(c); /// /// 根节点 /// public static T GetRoot(T c, Func selector) where T : ITreeParent => c.Parent != null ? GetRoot(c.Parent, selector) : c; /// /// 递归取出所有下级 /// /// /// /// private static List GetChildren(T t, Func> selector) { return selector(t).Union(selector(t).Where(c => selector(c)?.Any() == true).SelectMany(c => GetChildren(c, selector))).ToList(); } /// /// 递归取出所有上级 /// /// /// private static List GetParents(T t, Func selector) { var list = new List() { selector(t) }; if (selector(t) != null) { return list.Union(GetParents(selector(t), selector)).Where(x => x != null).ToList(); } list.RemoveAll(x => x == null); return list; } } public static class TreeExtensionLong { /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : ITreeEntity { return source.ToTree(); } } public static class TreeExtensionGuid { /// /// 平行集合转换成树形结构 /// /// /// /// public static List ToTree(this IEnumerable source) where T : ITreeEntity { return source.ToTree(); } } public static class TreeExtensionString { /// /// 平行集合转换成树形结构 /// /// /// /// /// public static List ToTree(this IEnumerable source, string topValue = null) where T : ITreeEntity { if (source is IQueryable queryable) { source = queryable.ToList(); } source = source.Where(t => t != null).ToList(); return TreeExtensions.BuildTree(source, topValue).ToList(); } } }