TreeExtensions.cs 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using Masuit.Tools.Systems;
  6. namespace Masuit.Tools.Models;
  7. /// <summary>
  8. /// 树形数据扩展
  9. /// </summary>
  10. public static class TreeExtensions
  11. {
  12. /// <summary>
  13. /// 过滤
  14. /// </summary>
  15. /// <typeparam name="T"></typeparam>
  16. /// <param name="items"></param>
  17. /// <param name="func"></param>
  18. /// <returns></returns>
  19. public static IEnumerable<T> Filter<T>(this IEnumerable<T> items, Func<T, bool> func) where T : class, ITreeChildren<T>
  20. {
  21. return Flatten(items).Where(func);
  22. }
  23. /// <summary>
  24. /// 过滤
  25. /// </summary>
  26. /// <typeparam name="T"></typeparam>
  27. /// <param name="items"></param>
  28. /// <param name="func"></param>
  29. /// <returns></returns>
  30. public static IEnumerable<Tree<T>> Filter<T>(this IEnumerable<Tree<T>> items, Func<Tree<T>, bool> func) where T : class
  31. {
  32. return Flatten(items).Where(func);
  33. }
  34. /// <summary>
  35. /// 过滤
  36. /// </summary>
  37. /// <typeparam name="T"></typeparam>
  38. /// <param name="item"></param>
  39. /// <param name="func"></param>
  40. /// <returns></returns>
  41. public static IEnumerable<T> Filter<T>(this T item, Func<T, bool> func) where T : class, ITreeChildren<T>
  42. {
  43. return Flatten(item).Where(func);
  44. }
  45. /// <summary>
  46. /// 过滤
  47. /// </summary>
  48. /// <typeparam name="T"></typeparam>
  49. /// <param name="item"></param>
  50. /// <param name="func"></param>
  51. /// <returns></returns>
  52. public static IEnumerable<Tree<T>> Filter<T>(this Tree<T> item, Func<Tree<T>, bool> func) where T : class
  53. {
  54. return Flatten(item).Where(func);
  55. }
  56. /// <summary>
  57. /// 平铺开
  58. /// </summary>
  59. /// <typeparam name="T"></typeparam>
  60. /// <param name="items"></param>
  61. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  62. /// <returns></returns>
  63. public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Action<T, T> optionAction = null) where T : class, ITreeChildren<T>
  64. {
  65. if (items == null)
  66. {
  67. yield break;
  68. }
  69. // 使用一个栈来存储待处理的节点
  70. var stack = new Stack<T>();
  71. // 首先将所有项压入栈中
  72. foreach (var item in items)
  73. {
  74. stack.Push(item);
  75. }
  76. // 循环直到栈为空
  77. while (stack.Count > 0)
  78. {
  79. // 弹出栈顶的节点
  80. var currentItem = stack.Pop();
  81. yield return currentItem;
  82. // 为当前节点设置子节点,如果optionAction不为空,则对每个子节点执行操作
  83. var children = currentItem.Children ?? new List<T>();
  84. foreach (var child in children)
  85. {
  86. optionAction?.Invoke(child, currentItem);
  87. stack.Push(child); // 将子节点压入栈中
  88. }
  89. }
  90. }
  91. /// <summary>
  92. /// 平铺开
  93. /// </summary>
  94. /// <typeparam name="T"></typeparam>
  95. /// <param name="p"></param>
  96. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  97. /// <returns></returns>
  98. public static IEnumerable<T> Flatten<T>(this T p, Action<T, T> optionAction = null) where T : class, ITreeChildren<T>
  99. {
  100. if (p == null)
  101. {
  102. yield break;
  103. }
  104. // 使用一个队列来存储待处理的节点
  105. var queue = new Queue<T>();
  106. queue.Enqueue(p);
  107. while (queue.Count > 0)
  108. {
  109. // 从队列中出队一个节点
  110. var currentItem = queue.Dequeue();
  111. // 将当前节点返回
  112. yield return currentItem;
  113. // 获取当前节点的子节点,如果子节点列表不存在,则初始化为空列表
  114. var children = currentItem.Children ?? new List<T>();
  115. // 将所有子节点入队
  116. foreach (var child in children)
  117. {
  118. // 执行平铺时的操作(如果有的话)
  119. optionAction?.Invoke(child, currentItem);
  120. queue.Enqueue(child);
  121. }
  122. }
  123. }
  124. /// <summary>
  125. /// 平铺开任意树形结构数据
  126. /// </summary>
  127. /// <typeparam name="T"></typeparam>
  128. /// <param name="items"></param>
  129. /// <param name="selector"></param>
  130. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  131. /// <returns></returns>
  132. public static IEnumerable<T> Flatten<T>(this IEnumerable<T> items, Func<T, IEnumerable<T>> selector, Action<T, T> optionAction = null)
  133. {
  134. if (items == null)
  135. {
  136. yield break;
  137. }
  138. // 使用一个队列来存储待处理的节点
  139. var queue = new Queue<T>();
  140. // 首先将所有项加入队列
  141. foreach (var item in items)
  142. {
  143. queue.Enqueue(item);
  144. }
  145. // 循环直到队列为空
  146. while (queue.Count > 0)
  147. {
  148. // 从队列中取出当前节点
  149. var currentItem = queue.Dequeue();
  150. // 将当前节点返回
  151. yield return currentItem;
  152. // 获取当前节点的所有子节点
  153. var children = selector(currentItem) ?? new List<T>();
  154. // 将所有子节点加入队列
  155. foreach (var child in children)
  156. {
  157. // 执行平铺时的操作(如果有的话)
  158. optionAction?.Invoke(child, currentItem);
  159. queue.Enqueue(child);
  160. }
  161. }
  162. }
  163. /// <summary>
  164. /// 平铺开
  165. /// </summary>
  166. /// <typeparam name="T"></typeparam>
  167. /// <param name="items"></param>
  168. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  169. /// <returns></returns>
  170. public static IEnumerable<Tree<T>> Flatten<T>(this IEnumerable<Tree<T>> items, Action<Tree<T>, Tree<T>> optionAction = null) where T : class
  171. {
  172. if (items == null)
  173. {
  174. yield break;
  175. }
  176. // 使用一个队列来存储待处理的节点
  177. var queue = new Queue<Tree<T>>();
  178. // 首先将所有项加入队列
  179. foreach (var item in items)
  180. {
  181. queue.Enqueue(item);
  182. }
  183. // 循环直到队列为空
  184. while (queue.Count > 0)
  185. {
  186. // 从队列中取出当前节点
  187. var currentItem = queue.Dequeue();
  188. // 将当前节点返回
  189. yield return currentItem;
  190. // 获取当前节点的所有子节点,如果子节点列表不存在,则初始化为空列表
  191. var children = currentItem.Children ?? new List<Tree<T>>();
  192. // 将所有子节点加入队列
  193. foreach (var child in children)
  194. {
  195. // 执行平铺时的操作(如果有的话)
  196. optionAction?.Invoke(child, currentItem);
  197. queue.Enqueue(child);
  198. }
  199. }
  200. }
  201. /// <summary>
  202. /// 平铺开
  203. /// </summary>
  204. /// <typeparam name="T"></typeparam>
  205. /// <param name="p"></param>
  206. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  207. /// <returns></returns>
  208. public static IEnumerable<Tree<T>> Flatten<T>(this Tree<T> p, Action<Tree<T>, Tree<T>> optionAction = null) where T : class
  209. {
  210. if (p == null)
  211. {
  212. yield break;
  213. }
  214. // 使用一个队列来存储待处理的节点
  215. var queue = new Queue<Tree<T>>();
  216. queue.Enqueue(p);
  217. // 遍历队列直到它为空
  218. while (queue.Count > 0)
  219. {
  220. // 从队列中取出当前节点
  221. var currentItem = queue.Dequeue();
  222. // 将当前节点返回
  223. yield return currentItem;
  224. // 获取当前节点的子节点列表,如果为空则初始化为空列表
  225. var children = currentItem.Children ?? new List<Tree<T>>();
  226. // 将所有子节点添加到队列中
  227. foreach (var child in children)
  228. {
  229. // 执行平铺时的操作(如果有的话)
  230. optionAction?.Invoke(child, currentItem);
  231. queue.Enqueue(child);
  232. }
  233. }
  234. }
  235. /// <summary>
  236. /// 平铺开任意树形结构数据
  237. /// </summary>
  238. /// <typeparam name="T"></typeparam>
  239. /// <param name="items"></param>
  240. /// <param name="selector"></param>
  241. /// <param name="optionAction">平铺时子级需要做的操作,参数1:子级对象,参数2:父级对象</param>
  242. /// <returns></returns>
  243. 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)
  244. {
  245. if (items == null)
  246. {
  247. yield break;
  248. }
  249. // 使用一个队列来存储待处理的节点
  250. var queue = new Queue<Tree<T>>();
  251. // 首先将所有项加入队列
  252. foreach (var item in items)
  253. {
  254. queue.Enqueue(item);
  255. }
  256. // 循环直到队列为空
  257. while (queue.Count > 0)
  258. {
  259. // 从队列中取出当前节点
  260. var currentItem = queue.Dequeue();
  261. // 将当前节点返回
  262. yield return currentItem;
  263. // 获取当前节点的所有子节点
  264. var children = selector(currentItem) ?? new List<Tree<T>>();
  265. // 将所有子节点加入队列
  266. foreach (var child in children)
  267. {
  268. // 执行平铺时的操作(如果有的话)
  269. optionAction?.Invoke(child, currentItem);
  270. queue.Enqueue(child);
  271. }
  272. }
  273. }
  274. /// <summary>
  275. /// 平行集合转换成树形结构
  276. /// </summary>
  277. /// <typeparam name="T"></typeparam>
  278. /// <param name="source"></param>
  279. /// <param name="idSelector"></param>
  280. /// <param name="pidSelector"></param>
  281. /// <param name="topValue">根对象parentId的值</param>
  282. /// <returns></returns>
  283. 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>
  284. {
  285. return ToTree<T, string>(source, idSelector, pidSelector, topValue);
  286. }
  287. /// <summary>
  288. /// 平行集合转换成树形结构
  289. /// </summary>
  290. /// <typeparam name="T"></typeparam>
  291. /// <param name="source"></param>
  292. /// <param name="idSelector"></param>
  293. /// <param name="pidSelector"></param>
  294. /// <param name="topValue">根对象parentId的值</param>
  295. /// <returns></returns>
  296. 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>
  297. {
  298. return ToTree<T, int>(source, idSelector, pidSelector, topValue);
  299. }
  300. /// <summary>
  301. /// 平行集合转换成树形结构
  302. /// </summary>
  303. /// <typeparam name="T"></typeparam>
  304. /// <param name="source"></param>
  305. /// <param name="idSelector"></param>
  306. /// <param name="pidSelector"></param>
  307. /// <param name="topValue">根对象parentId的值</param>
  308. /// <returns></returns>
  309. 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>
  310. {
  311. return ToTree<T, long>(source, idSelector, pidSelector, topValue);
  312. }
  313. /// <summary>
  314. /// 平行集合转换成树形结构
  315. /// </summary>
  316. /// <typeparam name="T"></typeparam>
  317. /// <param name="source"></param>
  318. /// <param name="idSelector"></param>
  319. /// <param name="pidSelector"></param>
  320. /// <param name="topValue">根对象parentId的值</param>
  321. /// <returns></returns>
  322. 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>
  323. {
  324. return ToTree<T, Guid>(source, idSelector, pidSelector, topValue);
  325. }
  326. /// <summary>
  327. /// 平行集合转换成树形结构
  328. /// </summary>
  329. /// <typeparam name="T"></typeparam>
  330. /// <typeparam name="TKey"></typeparam>
  331. /// <param name="source"></param>
  332. /// <param name="idSelector"></param>
  333. /// <param name="pidSelector"></param>
  334. /// <param name="topValue">根对象parentId的值</param>
  335. /// <returns></returns>
  336. 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
  337. {
  338. if (source is IQueryable<T> queryable)
  339. {
  340. source = queryable.ToList();
  341. }
  342. if (idSelector.Body.ToString() == pidSelector.Body.ToString())
  343. {
  344. throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
  345. }
  346. var pidFunc = pidSelector.Compile();
  347. var idFunc = idSelector.Compile();
  348. return BuildTree(source.Where(t => t != null), idFunc, pidFunc, topValue).ToList();
  349. }
  350. /// <summary>
  351. /// 平行集合转换成树形结构
  352. /// </summary>
  353. /// <typeparam name="T"></typeparam>
  354. /// <param name="source"></param>
  355. /// <returns></returns>
  356. public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, int>
  357. {
  358. return ToTree<T, int>(source);
  359. }
  360. /// <summary>
  361. /// 平行集合转换成树形结构
  362. /// </summary>
  363. /// <typeparam name="T"></typeparam>
  364. /// <typeparam name="TKey"></typeparam>
  365. /// <param name="source"></param>
  366. /// <param name="topValue"></param>
  367. /// <returns></returns>
  368. public static List<T> ToTree<T, TKey>(this IEnumerable<T> source, TKey? topValue = null) where T : class, ITreeEntity<T, TKey> where TKey : struct, IComparable
  369. {
  370. if (source is IQueryable<T> queryable)
  371. {
  372. source = queryable.ToList();
  373. }
  374. var list = source.Where(t => t != null).ToList();
  375. return BuildTree(list, topValue).ToList();
  376. }
  377. /// <summary>
  378. /// 平行集合转换成树形结构
  379. /// </summary>
  380. /// <typeparam name="T"></typeparam>
  381. /// <typeparam name="TKey"></typeparam>
  382. /// <param name="source"></param>
  383. /// <param name="idSelector"></param>
  384. /// <param name="pidSelector"></param>
  385. /// <param name="topValue">根对象parentId的值</param>
  386. /// <returns></returns>
  387. 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
  388. {
  389. if (source is IQueryable<T> queryable)
  390. {
  391. source = queryable.ToList();
  392. }
  393. if (idSelector.Body.ToString() == pidSelector.Body.ToString())
  394. {
  395. throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
  396. }
  397. var pidFunc = pidSelector.Compile();
  398. var idFunc = idSelector.Compile();
  399. source = source.Where(t => t != null).ToList();
  400. return BuildTree(source, idFunc, pidFunc, topValue).ToList();
  401. }
  402. 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
  403. {
  404. // 创建一个字典,用于快速查找节点的子节点
  405. var childrenLookup = new Dictionary<TKey, List<T>>();
  406. var list = source as ICollection<T> ?? source.ToList();
  407. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
  408. {
  409. childrenLookup[idSelector(item)] = new List<T>();
  410. }
  411. // 构建树结构
  412. foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item))))
  413. {
  414. childrenLookup[pidSelector(item)].Add(item);
  415. }
  416. // 找到根节点,即没有父节点的节点
  417. foreach (var root in list.Where(x => Equals(pidSelector(x), topValue)))
  418. {
  419. // 为根节点和所有子节点设置Children属性
  420. // 使用队列来模拟递归过程
  421. var queue = new Queue<T>();
  422. queue.Enqueue(root);
  423. while (queue.Count > 0)
  424. {
  425. // 出队当前节点
  426. var current = queue.Dequeue();
  427. // 为当前节点设置子节点
  428. if (childrenLookup.TryGetValue(idSelector(current), out var children))
  429. {
  430. current.Children = children;
  431. foreach (var child in children)
  432. {
  433. // 如果子节点实现了ITreeParent接口,则设置其Parent属性
  434. if (child is ITreeParent<T> tree)
  435. {
  436. tree.Parent = current;
  437. }
  438. // 将子节点入队以继续处理
  439. queue.Enqueue(child);
  440. }
  441. }
  442. }
  443. yield return root;
  444. }
  445. }
  446. internal static IEnumerable<T> BuildTree<T, TKey>(IEnumerable<T> source, TKey? topValue = default) where T : ITreeEntity<T, TKey> where TKey : struct, IComparable
  447. {
  448. // 创建一个字典,用于快速查找节点的子节点
  449. var childrenLookup = new NullableDictionary<TKey, List<T>>();
  450. var list = source as ICollection<T> ?? source.ToList();
  451. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(item.Id)))
  452. {
  453. childrenLookup[item.Id] = new List<T>();
  454. }
  455. // 构建树结构
  456. foreach (var item in list.Where(item => (item.ParentId != null || !Equals(item.ParentId, default(TKey))) && childrenLookup.ContainsKey(item.ParentId ?? default)))
  457. {
  458. childrenLookup[item.ParentId ?? default].Add(item);
  459. }
  460. // 找到根节点,即没有父节点的节点
  461. foreach (var root in list.Where(x => Equals(x.ParentId, topValue)))
  462. {
  463. // 为根节点和所有子节点设置Children属性
  464. // 使用队列来模拟递归过程
  465. var queue = new Queue<T>();
  466. queue.Enqueue(root);
  467. while (queue.Count > 0)
  468. {
  469. // 出队当前节点
  470. var current = queue.Dequeue();
  471. // 为当前节点设置子节点
  472. if (childrenLookup.TryGetValue(current.Id, out var children))
  473. {
  474. current.Children = children;
  475. foreach (var child in children)
  476. {
  477. // 如果子节点实现了ITreeParent接口,则设置其Parent属性
  478. if (child is ITreeParent<T> tree)
  479. {
  480. tree.Parent = current;
  481. }
  482. // 将子节点入队以继续处理
  483. queue.Enqueue(child);
  484. }
  485. }
  486. }
  487. yield return root;
  488. }
  489. }
  490. internal static IEnumerable<T> BuildTree<T>(IEnumerable<T> source, string topValue = null) where T : ITreeEntity<T>
  491. {
  492. // 创建一个字典,用于快速查找节点的子节点
  493. var childrenLookup = new NullableDictionary<string, List<T>>();
  494. var list = source as ICollection<T> ?? source.ToList();
  495. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(item.Id)))
  496. {
  497. childrenLookup[item.Id] = new List<T>();
  498. }
  499. // 构建树结构
  500. foreach (var item in list.Where(item => !string.IsNullOrEmpty(item.ParentId) && childrenLookup.ContainsKey(item.ParentId)))
  501. {
  502. childrenLookup[item.ParentId].Add(item);
  503. }
  504. // 找到根节点,即没有父节点的节点
  505. foreach (var root in list.Where(x => Equals(x.ParentId, topValue)))
  506. {
  507. // 为根节点和所有子节点设置Children属性
  508. // 使用队列来模拟递归过程
  509. var queue = new Queue<T>();
  510. queue.Enqueue(root);
  511. while (queue.Count > 0)
  512. {
  513. // 出队当前节点
  514. var current = queue.Dequeue();
  515. // 为当前节点设置子节点
  516. if (childrenLookup.TryGetValue(current.Id, out var children))
  517. {
  518. current.Children = children;
  519. foreach (var child in children)
  520. {
  521. // 如果子节点实现了ITreeParent接口,则设置其Parent属性
  522. if (child is ITreeParent<T> tree)
  523. {
  524. tree.Parent = current;
  525. }
  526. // 将子节点入队以继续处理
  527. queue.Enqueue(child);
  528. }
  529. }
  530. }
  531. yield return root;
  532. }
  533. }
  534. internal static IEnumerable<T> BuildTree<T>(IEnumerable<T> source, T parent) where T : ITreeEntity<T>
  535. {
  536. // 创建一个字典,用于快速查找节点的子节点
  537. var childrenLookup = new NullableDictionary<string, List<T>>();
  538. var list = source as ICollection<T> ?? source.ToList();
  539. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(item.Id)))
  540. {
  541. childrenLookup[item.Id] = new List<T>();
  542. }
  543. // 构建树结构
  544. foreach (var item in list.Where(item => !string.IsNullOrEmpty(item.ParentId) && childrenLookup.ContainsKey(item.ParentId)))
  545. {
  546. childrenLookup[item.ParentId].Add(item);
  547. }
  548. // 找到根节点,即没有父节点的节点
  549. foreach (var root in list.Where(x => x.Id == parent.Id))
  550. {
  551. // 为根节点和所有子节点设置Children属性
  552. // 使用队列来模拟递归过程
  553. var queue = new Queue<T>();
  554. queue.Enqueue(root);
  555. while (queue.Count > 0)
  556. {
  557. // 出队当前节点
  558. var current = queue.Dequeue();
  559. // 为当前节点设置子节点
  560. if (childrenLookup.TryGetValue(current.Id, out var children))
  561. {
  562. current.Children = children;
  563. foreach (var child in children)
  564. {
  565. // 如果子节点实现了ITreeParent接口,则设置其Parent属性
  566. if (child is ITreeParent<T> tree)
  567. {
  568. tree.Parent = current;
  569. }
  570. // 将子节点入队以继续处理
  571. queue.Enqueue(child);
  572. }
  573. }
  574. }
  575. yield return root;
  576. }
  577. }
  578. 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
  579. {
  580. // 创建一个字典,用于快速查找节点的子节点
  581. var childrenLookup = new NullableDictionary<TKey, List<T>>();
  582. var list = source as ICollection<T> ?? source.ToList();
  583. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
  584. {
  585. childrenLookup[idSelector(item)] = new List<T>();
  586. }
  587. // 构建树结构
  588. foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item) ?? default)))
  589. {
  590. childrenLookup[pidSelector(item) ?? default].Add(item);
  591. }
  592. // 找到根节点,即没有父节点的节点
  593. foreach (var root in list.Where(x => Equals(pidSelector(x), topValue)))
  594. {
  595. // 为根节点和所有子节点设置Children属性
  596. // 使用队列来模拟递归过程
  597. var queue = new Queue<T>();
  598. queue.Enqueue(root);
  599. while (queue.Count > 0)
  600. {
  601. // 出队当前节点
  602. var current = queue.Dequeue();
  603. // 为当前节点设置子节点
  604. if (childrenLookup.TryGetValue(idSelector(current), out var children))
  605. {
  606. current.Children = children;
  607. foreach (var child in children)
  608. {
  609. // 如果子节点实现了ITreeParent接口,则设置其Parent属性
  610. if (child is ITreeParent<T> tree)
  611. {
  612. tree.Parent = current;
  613. }
  614. // 将子节点入队以继续处理
  615. queue.Enqueue(child);
  616. }
  617. }
  618. }
  619. yield return root;
  620. }
  621. }
  622. /// <summary>
  623. /// 平行集合转换成树形结构
  624. /// </summary>
  625. /// <typeparam name="T"></typeparam>
  626. /// <typeparam name="TKey"></typeparam>
  627. /// <param name="source"></param>
  628. /// <param name="idSelector"></param>
  629. /// <param name="pidSelector"></param>
  630. /// <param name="topValue">根对象parentId的值</param>
  631. /// <returns></returns>
  632. 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
  633. {
  634. if (idSelector.Body.ToString() == pidSelector.Body.ToString())
  635. {
  636. throw new ArgumentException("idSelector和pidSelector不应该为同一字段!");
  637. }
  638. var pidFunc = pidSelector.Compile();
  639. var idFunc = idSelector.Compile();
  640. var list = source.Where(t => t != null).ToList();
  641. var temp = new List<Tree<T>>();
  642. foreach (var item in list.Where(item => pidFunc(item) is null || pidFunc(item).Equals(topValue)))
  643. {
  644. var parent = new Tree<T>(item);
  645. temp.AddRange(BuildTree(list, parent, idFunc, pidFunc));
  646. }
  647. return temp;
  648. }
  649. private static IEnumerable<Tree<T>> BuildTree<T, TKey>(List<T> list, Tree<T> parent, Func<T, TKey> idSelector, Func<T, TKey> pidSelector) where TKey : IComparable
  650. {
  651. // 创建一个字典,用于快速查找节点的子节点
  652. var childrenLookup = new Dictionary<TKey, List<Tree<T>>>();
  653. foreach (var item in list.Where(item => !childrenLookup.ContainsKey(idSelector(item))))
  654. {
  655. childrenLookup[idSelector(item)] = new List<Tree<T>>();
  656. }
  657. // 构建树结构
  658. foreach (var item in list.Where(item => !Equals(pidSelector(item), default(TKey)) && childrenLookup.ContainsKey(pidSelector(item))))
  659. {
  660. childrenLookup[pidSelector(item)].Add(new Tree<T>(item));
  661. }
  662. // 找到根节点,即没有父节点的节点
  663. foreach (var root in list.Where(x => Equals(pidSelector(x), idSelector(parent.Value))))
  664. {
  665. // 为根节点和所有子节点设置Children属性
  666. // 使用队列来模拟递归过程
  667. var queue = new Queue<Tree<T>>();
  668. queue.Enqueue(new Tree<T>(root));
  669. while (queue.Count > 0)
  670. {
  671. // 出队当前节点
  672. var current = queue.Dequeue();
  673. // 为当前节点设置子节点
  674. if (childrenLookup.TryGetValue(idSelector(current.Value), out var children))
  675. {
  676. current.Children = children;
  677. foreach (var child in children)
  678. {
  679. child.Parent = current.Parent;
  680. // 将子节点入队以继续处理
  681. queue.Enqueue(child);
  682. }
  683. }
  684. }
  685. yield return new Tree<T>(root);
  686. }
  687. }
  688. /// <summary>
  689. /// 所有子级
  690. /// </summary>
  691. public static ICollection<T> AllChildren<T>(this T tree) where T : ITreeChildren<T> => GetChildren(tree, c => c.Children);
  692. /// <summary>
  693. /// 所有子级
  694. /// </summary>
  695. public static ICollection<T> AllChildren<T>(this T tree, Func<T, IEnumerable<T>> selector) where T : ITreeChildren<T> => GetChildren(tree, selector);
  696. /// <summary>
  697. /// 所有子级
  698. /// </summary>
  699. public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree) => GetChildren(tree, c => c.Children);
  700. /// <summary>
  701. /// 所有子级
  702. /// </summary>
  703. public static ICollection<Tree<T>> AllChildren<T>(this Tree<T> tree, Func<Tree<T>, IEnumerable<Tree<T>>> selector) => GetChildren(tree, selector);
  704. /// <summary>
  705. /// 所有父级
  706. /// </summary>
  707. public static List<T> AllParent<T>(this T tree) where T : class, ITreeParent<T> => GetParents(tree, c => c.Parent);
  708. /// <summary>
  709. /// 所有父级
  710. /// </summary>
  711. public static List<T> AllParent<T>(this T tree, Func<T, T> selector) where T : class, ITreeParent<T> => GetParents(tree, selector);
  712. /// <summary>
  713. /// 所有父级
  714. /// </summary>
  715. public static List<Tree<T>> AllParent<T>(this Tree<T> tree, Func<Tree<T>, Tree<T>> selector) => GetParents(tree, selector);
  716. /// <summary>
  717. /// 是否是根节点
  718. /// </summary>
  719. public static bool IsRoot<T>(this ITreeParent<T> tree) where T : ITreeParent<T> => tree.Parent == null;
  720. /// <summary>
  721. /// 是否是叶子节点
  722. /// </summary>
  723. public static bool IsLeaf<T>(this ITreeChildren<T> tree) where T : ITreeChildren<T> => tree.Children?.Count == 0;
  724. /// <summary>
  725. /// 是否是根节点
  726. /// </summary>
  727. public static bool IsRoot<T>(this Tree<T> tree) => tree.Parent == null;
  728. /// <summary>
  729. /// 是否是叶子节点
  730. /// </summary>
  731. public static bool IsLeaf<T>(this Tree<T> tree) => tree.Children?.Count == 0;
  732. /// <summary>
  733. /// 深度层级
  734. /// </summary>
  735. public static int Level<T>(this T tree) where T : class, ITreeParent<T>
  736. {
  737. if (tree == null)
  738. {
  739. throw new ArgumentNullException(nameof(tree), "当前节点不能为null");
  740. }
  741. // 使用一个队列来存储待处理的节点
  742. var queue = new Queue<T>();
  743. queue.Enqueue(tree);
  744. int level = 1;
  745. // 循环直到队列为空
  746. while (queue.Count > 0)
  747. {
  748. int nodeCount = queue.Count; // 当前层级的节点数
  749. for (int i = 0; i < nodeCount; i++)
  750. {
  751. // 从队列中出队一个节点
  752. T currentNode = queue.Dequeue();
  753. // 如果当前节点是根节点,则返回当前层级
  754. if (currentNode.Parent == null)
  755. {
  756. return level;
  757. }
  758. // 将当前节点的父节点入队
  759. if (currentNode.Parent != null)
  760. {
  761. queue.Enqueue(currentNode.Parent);
  762. }
  763. }
  764. // 完成当前层级的遍历,准备进入下一层级
  765. level++;
  766. }
  767. // 如果执行到这里,说明没有找到根节点
  768. throw new InvalidOperationException("未找到根节点");
  769. }
  770. /// <summary>
  771. /// 节点路径(UNIX路径格式,以“/”分隔)
  772. /// </summary>
  773. public static string Path<T>(this T tree) where T : class, ITree<T> => GetFullPath(tree, t => t.Name);
  774. /// <summary>
  775. /// 节点路径(UNIX路径格式,以“/”分隔)
  776. /// </summary>
  777. public static string Path<T>(this T tree, Func<T, string> selector) where T : class, ITreeParent<T> => GetFullPath(tree, selector);
  778. /// <summary>
  779. /// 节点路径
  780. /// </summary>
  781. /// <param name="tree"></param>
  782. /// <param name="separator">分隔符</param>
  783. public static string Path<T>(this T tree, string separator) where T : class, ITree<T> => GetFullPath(tree, t => t.Name, separator);
  784. /// <summary>
  785. /// 节点路径
  786. /// </summary>
  787. /// <typeparam name="T"></typeparam>
  788. /// <param name="tree"></param>
  789. /// <param name="selector">选择字段</param>
  790. /// <param name="separator">分隔符</param>
  791. /// <returns></returns>
  792. public static string Path<T>(this T tree, Func<T, string> selector, string separator) where T : class, ITreeParent<T> => GetFullPath(tree, selector, separator);
  793. /// <summary>
  794. /// 根节点
  795. /// </summary>
  796. public static T Root<T>(this T tree) where T : class, ITreeParent<T> => GetRoot(tree, t => t.Parent);
  797. private static string GetFullPath<T>(T c, Func<T, string> selector, string separator = "/") where T : class, ITreeParent<T>
  798. {
  799. if (c == null || selector == null)
  800. {
  801. throw new ArgumentNullException(c == null ? nameof(c) : nameof(selector), "当前节点或选择器不能为null");
  802. }
  803. // 使用一个栈来存储节点,栈将逆序存储路径中的节点
  804. var stack = new Stack<T>();
  805. var currentNode = c;
  806. while (currentNode != null)
  807. {
  808. stack.Push(currentNode);
  809. currentNode = currentNode.Parent;
  810. }
  811. // 构建路径字符串
  812. return stack.Select(selector).Join(separator);
  813. }
  814. /// <summary>
  815. /// 根节点
  816. /// </summary>
  817. public static T GetRoot<T>(T c, Func<T, T> selector) where T : class, ITreeParent<T>
  818. {
  819. if (c == null || selector == null)
  820. {
  821. throw new ArgumentNullException(c == null ? nameof(c) : nameof(selector), "当前节点或父级选择器不能为null");
  822. }
  823. // 使用一个集合来存储已访问的节点,以避免无限循环
  824. var visited = new HashSet<T>();
  825. T currentNode = c;
  826. // 向上遍历直到找到根节点
  827. while (currentNode != null)
  828. {
  829. // 如果当前节点已被访问,说明存在循环引用,抛出异常
  830. if (!visited.Add(currentNode))
  831. {
  832. throw new InvalidOperationException("节点存在循环引用");
  833. }
  834. var parent = selector(currentNode);
  835. if (parent == null)
  836. {
  837. // 找到了根节点
  838. return currentNode;
  839. }
  840. currentNode = parent;
  841. }
  842. // 如果currentNode为null,说明最初的节点c就是根节点
  843. return c;
  844. }
  845. /// <summary>
  846. /// 递归取出所有下级
  847. /// </summary>
  848. /// <param name="t"></param>
  849. /// <param name="selector"></param>
  850. /// <returns></returns>
  851. private static List<T> GetChildren<T>(T t, Func<T, IEnumerable<T>> selector)
  852. {
  853. if (t == null || selector == null)
  854. {
  855. return new List<T>(); // 如果t或selector为null,则返回空列表
  856. }
  857. var list = new List<T>();
  858. var queue = new Queue<T>();
  859. queue.Enqueue(t);
  860. while (queue.Count > 0)
  861. {
  862. var current = queue.Dequeue();
  863. list.Add(current); // 将当前节点添加到结果列表
  864. var children = selector(current) ?? new List<T>();
  865. foreach (var child in children)
  866. {
  867. // 只有当子节点还有自己的子节点时,才将其加入队列
  868. if (selector(child).Any())
  869. {
  870. queue.Enqueue(child);
  871. }
  872. }
  873. }
  874. return list;
  875. }
  876. /// <summary>
  877. /// 递归取出所有上级
  878. /// </summary>
  879. /// <param name="t"></param>
  880. /// <param name="selector"></param>
  881. /// <returns></returns>
  882. private static List<T> GetParents<T>(T t, Func<T, T> selector) where T : class
  883. {
  884. if (t == null || selector == null)
  885. {
  886. return new List<T>(); // 如果t或selector为null,则返回空列表
  887. }
  888. var parents = new List<T>();
  889. var current = t;
  890. while (current != null)
  891. {
  892. // 添加当前节点到父节点列表
  893. parents.Add(current);
  894. // 通过selector函数获取当前节点的父节点
  895. current = selector(current);
  896. // 如果selector返回了已经遍历过的节点,则停止遍历以避免无限循环
  897. if (parents.Contains(current))
  898. {
  899. break;
  900. }
  901. }
  902. // 由于是逆序添加的,所以需要反转列表
  903. parents.Reverse();
  904. return parents;
  905. }
  906. }
  907. public static class TreeExtensionLong
  908. {
  909. /// <summary>
  910. /// 平行集合转换成树形结构
  911. /// </summary>
  912. /// <typeparam name="T"></typeparam>
  913. /// <param name="source"></param>
  914. /// <returns></returns>
  915. public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, long>
  916. {
  917. return source.ToTree<T, long>();
  918. }
  919. }
  920. public static class TreeExtensionGuid
  921. {
  922. /// <summary>
  923. /// 平行集合转换成树形结构
  924. /// </summary>
  925. /// <typeparam name="T"></typeparam>
  926. /// <param name="source"></param>
  927. /// <returns></returns>
  928. public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T, Guid>
  929. {
  930. return source.ToTree<T, Guid>();
  931. }
  932. }
  933. public static class TreeExtensionString
  934. {
  935. /// <summary>
  936. /// 平行集合转换成树形结构
  937. /// </summary>
  938. /// <typeparam name="T"></typeparam>
  939. /// <param name="source"></param>
  940. /// <param name="topValue"></param>
  941. /// <returns></returns>
  942. public static List<T> ToTree<T>(this IEnumerable<T> source, string topValue) where T : class, ITreeEntity<T>
  943. {
  944. if (source is IQueryable<T> queryable)
  945. {
  946. source = queryable.ToList();
  947. }
  948. source = source.Where(t => t != null).ToList();
  949. return TreeExtensions.BuildTree(source, topValue).ToList();
  950. }
  951. /// <summary>
  952. /// 平行集合转换成树形结构
  953. /// </summary>
  954. /// <typeparam name="T"></typeparam>
  955. /// <param name="source"></param>
  956. /// <returns></returns>
  957. public static List<T> ToTree<T>(this IEnumerable<T> source) where T : class, ITreeEntity<T>
  958. {
  959. if (source is IQueryable<T> queryable)
  960. {
  961. source = queryable.ToList();
  962. }
  963. source = source.Where(t => t != null).ToList();
  964. return source.Where(t => string.IsNullOrEmpty(t.ParentId)).SelectMany(parent => TreeExtensions.BuildTree(source, parent)).ToList();
  965. }
  966. }