Explorar el Código

ToTree函数非递归实现

懒得勤快 hace 1 año
padre
commit
ad1872329b

+ 1 - 1
Masuit.Tools.Abstractions/Masuit.Tools.Abstractions.csproj

@@ -3,7 +3,7 @@
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7;net8</TargetFrameworks>
         <TargetFrameworks>netstandard2.0;netstandard2.1;net461;net5;net6;net7;net8</TargetFrameworks>
         <LangVersion>latest</LangVersion>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2024.2</Version>
+        <Version>2024.3</Version>
         <Authors>懒得勤快</Authors>
         <Authors>懒得勤快</Authors>
         <Description>全龄段友好的C#万能工具库,码数吐司库,不管你是菜鸟新手还是骨灰级玩家都能轻松上手,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
         <Description>全龄段友好的C#万能工具库,码数吐司库,不管你是菜鸟新手还是骨灰级玩家都能轻松上手,Masuit.Tools基础公共库(适用于.NET4.6.1/.NET Standard2.0及以上项目),包含一些常用的操作类,大都是静态类,加密解密,反射操作,Excel简单导出,权重随机筛选算法,分布式短id,表达式树,linq扩展,文件压缩,多线程下载和FTP客户端,硬件信息,字符串扩展方法,日期时间扩展操作,中国农历,大文件拷贝,图像裁剪,验证码,断点续传,集合扩展等常用封装。
             官网教程:https://masuit.tools
             官网教程:https://masuit.tools

+ 176 - 57
Masuit.Tools.Abstractions/Models/TreeExtensions.cs

@@ -1,9 +1,8 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.Diagnostics;
 using System.Linq;
 using System.Linq;
 using System.Linq.Expressions;
 using System.Linq.Expressions;
-using Masuit.Tools.Reflection;
+using Masuit.Tools.Systems;
 
 
 namespace Masuit.Tools.Models
 namespace Masuit.Tools.Models
 {
 {
@@ -284,16 +283,7 @@ namespace Masuit.Tools.Models
 
 
             var pidFunc = pidSelector.Compile();
             var pidFunc = pidSelector.Compile();
             var idFunc = idSelector.Compile();
             var idFunc = idSelector.Compile();
-            source = source.Where(t => t != null);
-            var temp = new List<T>();
-            foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
-            {
-                item.Parent = default;
-                TransData(source, item, idFunc, pidFunc);
-                temp.Add(item);
-            }
-
-            return temp;
+            return TransData(source.Where(t => t != null), idFunc, pidFunc, topValue).ToList();
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -321,12 +311,11 @@ namespace Masuit.Tools.Models
                 source = queryable.ToList();
                 source = queryable.ToList();
             }
             }
 
 
-            source = source.Where(t => t != null);
+            source = source.Where(t => t != null).ToList();
             var temp = new List<T>();
             var temp = new List<T>();
             foreach (var item in source.Where(item => item.ParentId is null || item.ParentId.Equals(default)))
             foreach (var item in source.Where(item => item.ParentId is null || item.ParentId.Equals(default)))
             {
             {
-                TransData<T, TKey>(source, item);
-                temp.Add(item);
+                temp.AddRange(TransData<T, TKey>(source, item));
             }
             }
 
 
             return temp;
             return temp;
@@ -351,79 +340,210 @@ namespace Masuit.Tools.Models
 
 
             var pidFunc = pidSelector.Compile();
             var pidFunc = pidSelector.Compile();
             var idFunc = idSelector.Compile();
             var idFunc = idSelector.Compile();
-            source = source.Where(t => t != null);
+            source = source.Where(t => t != null).ToList();
             var temp = new List<T>();
             var temp = new List<T>();
             foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
             foreach (var item in source.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
             {
             {
-                TransData(source, item, idFunc, pidFunc);
-                temp.Add(item);
+                temp.AddRange(TransData(source, item, idFunc, pidFunc));
             }
             }
 
 
             return temp;
             return temp;
         }
         }
 
 
-        private static void TransData<T, TKey>(IEnumerable<T> source, T parent, Func<T, TKey> idSelector, Func<T, TKey> pidSelector) where T : ITreeChildren<T> where TKey : IComparable
+        private static IEnumerable<T> TransData<T, TKey>(IEnumerable<T> source, Func<T, TKey> idSelector, Func<T, TKey> pidSelector, TKey topValue = default) where T : ITreeChildren<T> where TKey : IComparable
         {
         {
-            var temp = new List<T>();
-            foreach (var item in source.Where(item => pidSelector(item)?.Equals(idSelector(parent)) == true))
+            // 创建一个字典,用于快速查找节点的子节点
+            var childrenLookup = new Dictionary<TKey, List<T>>();
+            foreach (var item in source.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
+            {
+                childrenLookup[idSelector(item)] = new List<T>();
+            }
+
+            // 构建树结构
+            foreach (var item in source.Where(item => Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item))))
             {
             {
-                TransData(source, item, idSelector, pidSelector);
-                if (item is ITreeParent<T> c)
+                childrenLookup[pidSelector(item)].Add(item);
+            }
+
+            // 找到根节点,即没有父节点的节点
+            foreach (var root in source.Where(x => Equals(pidSelector(x), topValue)))
+            {
+                // 为根节点和所有子节点设置Children属性
+                // 使用队列来模拟递归过程
+                var queue = new Queue<T>();
+                queue.Enqueue(root);
+
+                while (queue.Count > 0)
                 {
                 {
-                    c.Parent = parent;
+                    // 出队当前节点
+                    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<T> tree)
+                            {
+                                tree.Parent = current;
+                            }
+
+                            // 将子节点入队以继续处理
+                            queue.Enqueue(child);
+                        }
+                    }
                 }
                 }
-                temp.Add(item);
+                yield return root;
             }
             }
-
-            parent.Children = temp;
         }
         }
 
 
-        internal static void TransData<T, TKey>(IEnumerable<T> source, T parent) where T : ITreeEntity<T, TKey> where TKey : struct, IComparable
+        internal static IEnumerable<T> TransData<T, TKey>(IEnumerable<T> source, T parent) where T : ITreeEntity<T, TKey> where TKey : struct, IComparable
         {
         {
-            var temp = new List<T>();
-            foreach (var item in source.Where(item => item.ParentId?.Equals(parent.Id) == true))
+            // 创建一个字典,用于快速查找节点的子节点
+            var childrenLookup = new Dictionary<TKey?, List<T>>();
+            foreach (var item in source.Where(item => !childrenLookup.ContainsKey(item.Id)))
+            {
+                childrenLookup[item.Id] = new List<T>();
+            }
+
+            // 构建树结构
+            foreach (var item in source.Where(item => Equals(item.ParentId, default(TKey)) && childrenLookup.ContainsKey(item.ParentId)))
+            {
+                childrenLookup[item.ParentId].Add(item);
+            }
+
+            // 找到根节点,即没有父节点的节点
+            foreach (var root in source.Where(x => Equals(x.ParentId, parent.Id)))
             {
             {
-                TransData<T, TKey>(source, item);
-                if (item is ITreeParent<T> c)
+                // 为根节点和所有子节点设置Children属性
+                // 使用队列来模拟递归过程
+                var queue = new Queue<T>();
+                queue.Enqueue(root);
+
+                while (queue.Count > 0)
                 {
                 {
-                    c.Parent = parent;
+                    // 出队当前节点
+                    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<T> tree)
+                            {
+                                tree.Parent = current;
+                            }
+
+                            // 将子节点入队以继续处理
+                            queue.Enqueue(child);
+                        }
+                    }
                 }
                 }
-                temp.Add(item);
+                yield return root;
             }
             }
-
-            parent.Children = temp;
         }
         }
 
 
-        internal static void TransData<T>(IEnumerable<T> source, T parent) where T : ITreeEntity<T>
+        internal static IEnumerable<T> TransData<T>(IEnumerable<T> source, T parent) where T : ITreeEntity<T>
         {
         {
-            var temp = new List<T>();
-            foreach (var item in source.Where(item => item.ParentId?.Equals(parent.Id) == true))
+            // 创建一个字典,用于快速查找节点的子节点
+            var childrenLookup = new NullableDictionary<string, List<T>>();
+            foreach (var item in source.Where(item => !childrenLookup.ContainsKey(item.Id)))
             {
             {
-                TransData(source, item);
-                if (item is ITreeParent<T> c)
+                childrenLookup[item.Id] = new List<T>();
+            }
+
+            // 构建树结构
+            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, parent.Id)))
+            {
+                // 为根节点和所有子节点设置Children属性
+                // 使用队列来模拟递归过程
+                var queue = new Queue<T>();
+                queue.Enqueue(root);
+
+                while (queue.Count > 0)
                 {
                 {
-                    c.Parent = parent;
+                    // 出队当前节点
+                    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<T> tree)
+                            {
+                                tree.Parent = current;
+                            }
+
+                            // 将子节点入队以继续处理
+                            queue.Enqueue(child);
+                        }
+                    }
                 }
                 }
-                temp.Add(item);
+                yield return root;
             }
             }
-
-            parent.Children = temp;
         }
         }
 
 
-        private static void TransData<T, TKey>(IEnumerable<T> source, T parent, Func<T, TKey> idSelector, Func<T, TKey?> pidSelector) where T : ITreeChildren<T> where TKey : struct
+        private static IEnumerable<T> TransData<T, TKey>(IEnumerable<T> source, T parent, Func<T, TKey> idSelector, Func<T, TKey?> pidSelector) where T : ITreeChildren<T> where TKey : struct
         {
         {
-            var temp = new List<T>();
-            foreach (var item in source.Where(item => pidSelector(item)?.Equals(idSelector(parent)) == true))
+            // 创建一个字典,用于快速查找节点的子节点
+            var childrenLookup = new NullableDictionary<TKey, List<T>>();
+            foreach (var item in source.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
+            {
+                childrenLookup[idSelector(item)] = new List<T>();
+            }
+
+            // 构建树结构
+            foreach (var item in source.Where(item => Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item).Value)))
+            {
+                childrenLookup[pidSelector(item).Value].Add(item);
+            }
+
+            // 找到根节点,即没有父节点的节点
+            foreach (var root in source.Where(x => Equals(pidSelector(x), idSelector(parent))))
             {
             {
-                TransData(source, item, idSelector, pidSelector);
-                if (item is ITreeParent<T> c)
+                // 为根节点和所有子节点设置Children属性
+                // 使用队列来模拟递归过程
+                var queue = new Queue<T>();
+                queue.Enqueue(root);
+
+                while (queue.Count > 0)
                 {
                 {
-                    c.Parent = parent;
+                    // 出队当前节点
+                    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<T> tree)
+                            {
+                                tree.Parent = current;
+                            }
+
+                            // 将子节点入队以继续处理
+                            queue.Enqueue(child);
+                        }
+                    }
                 }
                 }
-                temp.Add(item);
+                yield return root;
             }
             }
-
-            parent.Children = temp;
         }
         }
 
 
         /// <summary>
         /// <summary>
@@ -642,12 +762,11 @@ namespace Masuit.Tools.Models
                 source = queryable.ToList();
                 source = queryable.ToList();
             }
             }
 
 
-            source = source.Where(t => t != null);
+            source = source.Where(t => t != null).ToList();
             var temp = new List<T>();
             var temp = new List<T>();
             foreach (var item in source.Where(item => item.ParentId is null || item.ParentId.Equals(default)))
             foreach (var item in source.Where(item => item.ParentId is null || item.ParentId.Equals(default)))
             {
             {
-                TreeExtensions.TransData(source, item);
-                temp.Add(item);
+                temp.AddRange(TreeExtensions.TransData(source, item));
             }
             }
 
 
             return temp;
             return temp;

+ 5 - 0
Masuit.Tools.Abstractions/Systems/NullableConcurrentDictionary.cs

@@ -127,6 +127,11 @@ public class NullableConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<N
         set => base[new NullObject<TKey>(key)] = value;
         set => base[new NullObject<TKey>(key)] = value;
     }
     }
 
 
+    public bool ContainsKey(TKey key)
+    {
+        return base.ContainsKey(new NullObject<TKey>(key));
+    }
+
     /// <summary>
     /// <summary>
     /// 隐式转换
     /// 隐式转换
     /// </summary>
     /// </summary>

+ 5 - 0
Masuit.Tools.Abstractions/Systems/NullableDictionary.cs

@@ -143,6 +143,11 @@ public class NullableDictionary<TKey, TValue> : Dictionary<NullObject<TKey>, TVa
         set => base[new NullObject<TKey>(key)] = value;
         set => base[new NullObject<TKey>(key)] = value;
     }
     }
 
 
+    public bool ContainsKey(TKey key)
+    {
+        return base.ContainsKey(new NullObject<TKey>(key));
+    }
+
     /// <summary>
     /// <summary>
     /// 隐式转换
     /// 隐式转换
     /// </summary>
     /// </summary>

+ 4 - 4
Masuit.Tools.Abstractions/Validator/IsEmailAttribute.cs

@@ -34,11 +34,11 @@ public class IsEmailAttribute : ValidationAttribute
     /// <summary>
     /// <summary>
     /// 可在配置文件AppSetting节中添加EmailDomainWhiteList配置邮箱域名白名单,EmailDomainBlockList配置邮箱域名黑名单,英文分号(;)或感叹号(!)或逗号(,)分隔,每个单独的元素支持正则表达式
     /// 可在配置文件AppSetting节中添加EmailDomainWhiteList配置邮箱域名白名单,EmailDomainBlockList配置邮箱域名黑名单,英文分号(;)或感叹号(!)或逗号(,)分隔,每个单独的元素支持正则表达式
     /// </summary>
     /// </summary>
-    /// <param name="valid">是否检查邮箱的有效性</param>
+    /// <param name="validDns">是否检查邮箱DNS记录的有效性</param>
     /// <param name="customMessage">自定义错误消息</param>
     /// <param name="customMessage">自定义错误消息</param>
-    public IsEmailAttribute(bool valid = true, string customMessage = null)
+    public IsEmailAttribute(bool validDns = true, string customMessage = null)
     {
     {
-        _valid = valid;
+        _valid = validDns;
         _customMessage = customMessage;
         _customMessage = customMessage;
     }
     }
 
 
@@ -121,4 +121,4 @@ public class IsEmailAttribute : ValidationAttribute
         ErrorMessage = _customMessage ?? "您输入的邮箱格式不正确!";
         ErrorMessage = _customMessage ?? "您输入的邮箱格式不正确!";
         return false;
         return false;
     }
     }
-}
+}

+ 1 - 1
Masuit.Tools.AspNetCore/Masuit.Tools.AspNetCore.csproj

@@ -18,7 +18,7 @@
         <Product>Masuit.Tools.AspNetCore</Product>
         <Product>Masuit.Tools.AspNetCore</Product>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <PackageId>Masuit.Tools.AspNetCore</PackageId>
         <LangVersion>latest</LangVersion>
         <LangVersion>latest</LangVersion>
-        <Version>2024.2</Version>
+        <Version>2024.3</Version>
         <RepositoryType></RepositoryType>
         <RepositoryType></RepositoryType>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <GeneratePackageOnBuild>True</GeneratePackageOnBuild>
         <FileVersion>1.1.9</FileVersion>
         <FileVersion>1.1.9</FileVersion>

+ 1 - 1
Masuit.Tools.Core/Masuit.Tools.Core.csproj

@@ -6,7 +6,7 @@
 官网教程:https://tools.masuit.org
 官网教程:https://tools.masuit.org
 github:https://github.com/ldqk/Masuit.Tools
 github:https://github.com/ldqk/Masuit.Tools
         </Description>
         </Description>
-        <Version>2024.2</Version>
+        <Version>2024.3</Version>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <Copyright>Copyright © 懒得勤快</Copyright>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageProjectUrl>https://github.com/ldqk/Masuit.Tools</PackageProjectUrl>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>
         <PackageTags>Masuit.Tools,工具库,Utility,Crypt,Extensions</PackageTags>

+ 1 - 1
Masuit.Tools.Excel/Masuit.Tools.Excel.csproj

@@ -3,7 +3,7 @@
         <TargetFramework>netstandard2.0</TargetFramework>
         <TargetFramework>netstandard2.0</TargetFramework>
         <LangVersion>latest</LangVersion>
         <LangVersion>latest</LangVersion>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
         <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
-        <Version>2024.2</Version>
+        <Version>2024.3</Version>
         <Authors>懒得勤快</Authors>
         <Authors>懒得勤快</Authors>
         <Description>Masuit.Tools.Excel导出库,支持一些简单数据的导出,支持图片列</Description>
         <Description>Masuit.Tools.Excel导出库,支持一些简单数据的导出,支持图片列</Description>
         <Copyright>懒得勤快</Copyright>
         <Copyright>懒得勤快</Copyright>

+ 1 - 1
Masuit.Tools.Net45/package.nuspec

@@ -2,7 +2,7 @@
 <package>
 <package>
     <metadata>
     <metadata>
         <id>Masuit.Tools.Net45</id>
         <id>Masuit.Tools.Net45</id>
-        <version>2024.2</version>
+        <version>2024.3</version>
         <title>Masuit.Tools</title>
         <title>Masuit.Tools</title>
         <authors>懒得勤快</authors>
         <authors>懒得勤快</authors>
         <owners>masuit.com</owners>
         <owners>masuit.com</owners>

+ 1 - 1
Masuit.Tools/package.nuspec

@@ -2,7 +2,7 @@
 <package>
 <package>
     <metadata>
     <metadata>
         <id>Masuit.Tools.Net</id>
         <id>Masuit.Tools.Net</id>
-        <version>2024.2</version>
+        <version>2024.3</version>
         <title>Masuit.Tools</title>
         <title>Masuit.Tools</title>
         <authors>懒得勤快</authors>
         <authors>懒得勤快</authors>
         <owners>masuit.com</owners>
         <owners>masuit.com</owners>