TreeExtensions.cs 32 KB

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