TreeExtensions.cs 38 KB

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