IEnumerableExtensions.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Linq;
  6. using System.Linq.Expressions;
  7. using System.Threading;
  8. using System.Threading.Tasks;
  9. namespace Masuit.Tools
  10. {
  11. public static partial class IEnumerableExtensions
  12. {
  13. #if NET6_0
  14. #else
  15. /// <summary>
  16. /// 按字段去重
  17. /// </summary>
  18. /// <typeparam name="TSource"></typeparam>
  19. /// <typeparam name="TKey"></typeparam>
  20. /// <param name="source"></param>
  21. /// <param name="keySelector"></param>
  22. /// <returns></returns>
  23. public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
  24. {
  25. var hash = new HashSet<TKey>();
  26. return source.Where(p => hash.Add(keySelector(p)));
  27. }
  28. #endif
  29. /// <summary>
  30. /// 添加多个元素
  31. /// </summary>
  32. /// <typeparam name="T"></typeparam>
  33. /// <param name="this"></param>
  34. /// <param name="values"></param>
  35. public static void AddRange<T>(this ICollection<T> @this, params T[] values)
  36. {
  37. foreach (var obj in values)
  38. {
  39. @this.Add(obj);
  40. }
  41. }
  42. /// <summary>
  43. /// 添加符合条件的多个元素
  44. /// </summary>
  45. /// <typeparam name="T"></typeparam>
  46. /// <param name="this"></param>
  47. /// <param name="predicate"></param>
  48. /// <param name="values"></param>
  49. public static void AddRangeIf<T>(this ICollection<T> @this, Func<T, bool> predicate, params T[] values)
  50. {
  51. foreach (var obj in values)
  52. {
  53. if (predicate(obj))
  54. {
  55. @this.Add(obj);
  56. }
  57. }
  58. }
  59. /// <summary>
  60. /// 添加不重复的元素
  61. /// </summary>
  62. /// <typeparam name="T"></typeparam>
  63. /// <param name="this"></param>
  64. /// <param name="values"></param>
  65. public static void AddRangeIfNotContains<T>(this ICollection<T> @this, params T[] values)
  66. {
  67. foreach (T obj in values)
  68. {
  69. if ([email protected](obj))
  70. {
  71. @this.Add(obj);
  72. }
  73. }
  74. }
  75. /// <summary>
  76. /// 移除符合条件的元素
  77. /// </summary>
  78. /// <typeparam name="T"></typeparam>
  79. /// <param name="this"></param>
  80. /// <param name="where"></param>
  81. public static void RemoveWhere<T>(this ICollection<T> @this, Func<T, bool> @where)
  82. {
  83. foreach (var obj in @this.Where(where).ToList())
  84. {
  85. @this.Remove(obj);
  86. }
  87. }
  88. /// <summary>
  89. /// 在元素之后添加元素
  90. /// </summary>
  91. /// <typeparam name="T"></typeparam>
  92. /// <param name="list"></param>
  93. /// <param name="condition">条件</param>
  94. /// <param name="value">值</param>
  95. public static void InsertAfter<T>(this IList<T> list, Func<T, bool> condition, T value)
  96. {
  97. foreach (var item in list.Select((item, index) => new { item, index }).Where(p => condition(p.item)).OrderByDescending(p => p.index))
  98. {
  99. if (item.index + 1 == list.Count)
  100. {
  101. list.Add(value);
  102. }
  103. else
  104. {
  105. list.Insert(item.index + 1, value);
  106. }
  107. }
  108. }
  109. /// <summary>
  110. /// 在元素之后添加元素
  111. /// </summary>
  112. /// <typeparam name="T"></typeparam>
  113. /// <param name="list"></param>
  114. /// <param name="index">索引位置</param>
  115. /// <param name="value">值</param>
  116. public static void InsertAfter<T>(this IList<T> list, int index, T value)
  117. {
  118. foreach (var item in list.Select((v, i) => new { Value = v, Index = i }).Where(p => p.Index == index).OrderByDescending(p => p.Index))
  119. {
  120. if (item.Index + 1 == list.Count)
  121. {
  122. list.Add(value);
  123. }
  124. else
  125. {
  126. list.Insert(item.Index + 1, value);
  127. }
  128. }
  129. }
  130. /// <summary>
  131. /// 转HashSet
  132. /// </summary>
  133. /// <typeparam name="T"></typeparam>
  134. /// <typeparam name="TResult"></typeparam>
  135. /// <param name="source"></param>
  136. /// <param name="selector"></param>
  137. /// <returns></returns>
  138. public static HashSet<TResult> ToHashSet<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector)
  139. {
  140. var set = new HashSet<TResult>();
  141. set.UnionWith(source.Select(selector));
  142. return set;
  143. }
  144. /// <summary>
  145. /// 遍历IEnumerable
  146. /// </summary>
  147. /// <param name="objs"></param>
  148. /// <param name="action">回调方法</param>
  149. /// <typeparam name="T"></typeparam>
  150. public static void ForEach<T>(this IEnumerable<T> objs, Action<T> action)
  151. {
  152. foreach (var o in objs)
  153. {
  154. action(o);
  155. }
  156. }
  157. /// <summary>
  158. /// 异步foreach
  159. /// </summary>
  160. /// <typeparam name="T"></typeparam>
  161. /// <param name="source"></param>
  162. /// <param name="maxParallelCount">最大并行数</param>
  163. /// <param name="action"></param>
  164. /// <param name="cancellationToken"></param>
  165. /// <returns></returns>
  166. public static async Task ForeachAsync<T>(this IEnumerable<T> source, Func<T, Task> action, int maxParallelCount, CancellationToken cancellationToken = default)
  167. {
  168. if (Debugger.IsAttached)
  169. {
  170. foreach (var item in source)
  171. {
  172. await action(item);
  173. }
  174. return;
  175. }
  176. var list = new List<Task>();
  177. foreach (var item in source)
  178. {
  179. if (cancellationToken.IsCancellationRequested)
  180. {
  181. return;
  182. }
  183. list.Add(action(item));
  184. if (list.Count >= maxParallelCount)
  185. {
  186. await Task.WhenAll(list);
  187. list.Clear();
  188. }
  189. }
  190. await Task.WhenAll(list);
  191. }
  192. /// <summary>
  193. /// 异步foreach
  194. /// </summary>
  195. /// <typeparam name="T"></typeparam>
  196. /// <param name="source"></param>
  197. /// <param name="action"></param>
  198. /// <returns></returns>
  199. public static Task ForeachAsync<T>(this IEnumerable<T> source, Func<T, Task> action, CancellationToken cancellationToken = default)
  200. {
  201. return ForeachAsync(source, action, source.Count(), cancellationToken);
  202. }
  203. /// <summary>
  204. /// 异步Select
  205. /// </summary>
  206. /// <typeparam name="T"></typeparam>
  207. /// <typeparam name="TResult"></typeparam>
  208. /// <param name="source"></param>
  209. /// <param name="selector"></param>
  210. /// <returns></returns>
  211. public static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, Task<TResult>> selector)
  212. {
  213. return Task.WhenAll(source.Select(selector));
  214. }
  215. /// <summary>
  216. /// 异步Select
  217. /// </summary>
  218. /// <typeparam name="T"></typeparam>
  219. /// <typeparam name="TResult"></typeparam>
  220. /// <param name="source"></param>
  221. /// <param name="selector"></param>
  222. /// <returns></returns>
  223. public static Task<TResult[]> SelectAsync<T, TResult>(this IEnumerable<T> source, Func<T, int, Task<TResult>> selector)
  224. {
  225. return Task.WhenAll(source.Select(selector));
  226. }
  227. /// <summary>
  228. /// 异步For
  229. /// </summary>
  230. /// <typeparam name="T"></typeparam>
  231. /// <param name="source"></param>
  232. /// <param name="selector"></param>
  233. /// <param name="maxParallelCount">最大并行数</param>
  234. /// <param name="cancellationToken">取消口令</param>
  235. /// <returns></returns>
  236. public static async Task ForAsync<T>(this IEnumerable<T> source, Func<T, int, Task> selector, int maxParallelCount, CancellationToken cancellationToken = default)
  237. {
  238. int index = 0;
  239. if (Debugger.IsAttached)
  240. {
  241. foreach (var item in source)
  242. {
  243. await selector(item, index);
  244. index++;
  245. }
  246. return;
  247. }
  248. var list = new List<Task>();
  249. foreach (var item in source)
  250. {
  251. if (cancellationToken.IsCancellationRequested)
  252. {
  253. return;
  254. }
  255. list.Add(selector(item, index));
  256. Interlocked.Add(ref index, 1);
  257. if (list.Count >= maxParallelCount)
  258. {
  259. await Task.WhenAll(list);
  260. list.Clear();
  261. }
  262. }
  263. await Task.WhenAll(list);
  264. }
  265. /// <summary>
  266. /// 异步For
  267. /// </summary>
  268. /// <typeparam name="T"></typeparam>
  269. /// <param name="source"></param>
  270. /// <param name="selector"></param>
  271. /// <param name="cancellationToken">取消口令</param>
  272. /// <returns></returns>
  273. public static Task ForAsync<T>(this IEnumerable<T> source, Func<T, int, Task> selector, CancellationToken cancellationToken = default)
  274. {
  275. return ForAsync(source, selector, source.Count(), cancellationToken);
  276. }
  277. /// <summary>
  278. /// 取最大值
  279. /// </summary>
  280. /// <typeparam name="TSource"></typeparam>
  281. /// <typeparam name="TResult"></typeparam>
  282. /// <param name="source"></param>
  283. /// <param name="selector"></param>
  284. /// <returns></returns>
  285. public static TResult MaxOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector) => source.Select(selector).DefaultIfEmpty().Max();
  286. /// <summary>
  287. /// 取最大值
  288. /// </summary>
  289. /// <typeparam name="TSource"></typeparam>
  290. /// <typeparam name="TResult"></typeparam>
  291. /// <param name="source"></param>
  292. /// <param name="selector"></param>
  293. /// <param name="defaultValue"></param>
  294. /// <returns></returns>
  295. public static TResult MaxOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max();
  296. /// <summary>
  297. /// 取最大值
  298. /// </summary>
  299. /// <typeparam name="TSource"></typeparam>
  300. /// <param name="source"></param>
  301. /// <returns></returns>
  302. public static TSource MaxOrDefault<TSource>(this IQueryable<TSource> source) => source.DefaultIfEmpty().Max();
  303. /// <summary>
  304. /// 取最大值
  305. /// </summary>
  306. /// <typeparam name="TSource"></typeparam>
  307. /// <param name="source"></param>
  308. /// <param name="defaultValue"></param>
  309. /// <returns></returns>
  310. public static TSource MaxOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max();
  311. /// <summary>
  312. /// 取最大值
  313. /// </summary>
  314. /// <typeparam name="TSource"></typeparam>
  315. /// <typeparam name="TResult"></typeparam>
  316. /// <param name="source"></param>
  317. /// <param name="selector"></param>
  318. /// <param name="defaultValue"></param>
  319. /// <returns></returns>
  320. public static TResult MaxOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Max();
  321. /// <summary>
  322. /// 取最大值
  323. /// </summary>
  324. /// <typeparam name="TSource"></typeparam>
  325. /// <typeparam name="TResult"></typeparam>
  326. /// <param name="source"></param>
  327. /// <param name="selector"></param>
  328. /// <returns></returns>
  329. public static TResult MaxOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) => source.Select(selector).DefaultIfEmpty().Max();
  330. /// <summary>
  331. /// 取最大值
  332. /// </summary>
  333. /// <typeparam name="TSource"></typeparam>
  334. /// <param name="source"></param>
  335. /// <returns></returns>
  336. public static TSource MaxOrDefault<TSource>(this IEnumerable<TSource> source) => source.DefaultIfEmpty().Max();
  337. /// <summary>
  338. /// 取最大值
  339. /// </summary>
  340. /// <typeparam name="TSource"></typeparam>
  341. /// <param name="source"></param>
  342. /// <param name="defaultValue"></param>
  343. /// <returns></returns>
  344. public static TSource MaxOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Max();
  345. /// <summary>
  346. /// 取最小值
  347. /// </summary>
  348. /// <typeparam name="TSource"></typeparam>
  349. /// <typeparam name="TResult"></typeparam>
  350. /// <param name="source"></param>
  351. /// <param name="selector"></param>
  352. /// <returns></returns>
  353. public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector) => source.Select(selector).DefaultIfEmpty().Min();
  354. /// <summary>
  355. /// 取最小值
  356. /// </summary>
  357. /// <typeparam name="TSource"></typeparam>
  358. /// <typeparam name="TResult"></typeparam>
  359. /// <param name="source"></param>
  360. /// <param name="selector"></param>
  361. /// <param name="defaultValue"></param>
  362. /// <returns></returns>
  363. public static TResult MinOrDefault<TSource, TResult>(this IQueryable<TSource> source, Expression<Func<TSource, TResult>> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min();
  364. /// <summary>
  365. /// 取最小值
  366. /// </summary>
  367. /// <typeparam name="TSource"></typeparam>
  368. /// <param name="source"></param>
  369. /// <returns></returns>
  370. public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source) => source.DefaultIfEmpty().Min();
  371. /// <summary>
  372. /// 取最小值
  373. /// </summary>
  374. /// <typeparam name="TSource"></typeparam>
  375. /// <param name="source"></param>
  376. /// <param name="defaultValue"></param>
  377. /// <returns></returns>
  378. public static TSource MinOrDefault<TSource>(this IQueryable<TSource> source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min();
  379. /// <summary>
  380. /// 取最小值
  381. /// </summary>
  382. /// <typeparam name="TSource"></typeparam>
  383. /// <typeparam name="TResult"></typeparam>
  384. /// <param name="source"></param>
  385. /// <param name="selector"></param>
  386. /// <returns></returns>
  387. public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector) => source.Select(selector).DefaultIfEmpty().Min();
  388. /// <summary>
  389. /// 取最小值
  390. /// </summary>
  391. /// <typeparam name="TSource"></typeparam>
  392. /// <typeparam name="TResult"></typeparam>
  393. /// <param name="source"></param>
  394. /// <param name="selector"></param>
  395. /// <param name="defaultValue"></param>
  396. /// <returns></returns>
  397. public static TResult MinOrDefault<TSource, TResult>(this IEnumerable<TSource> source, Func<TSource, TResult> selector, TResult defaultValue) => source.Select(selector).DefaultIfEmpty(defaultValue).Min();
  398. /// <summary>
  399. /// 取最小值
  400. /// </summary>
  401. /// <typeparam name="TSource"></typeparam>
  402. /// <param name="source"></param>
  403. /// <returns></returns>
  404. public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source) => source.DefaultIfEmpty().Min();
  405. /// <summary>
  406. /// 取最小值
  407. /// </summary>
  408. /// <typeparam name="TSource"></typeparam>
  409. /// <param name="source"></param>
  410. /// <param name="defaultValue"></param>
  411. /// <returns></returns>
  412. public static TSource MinOrDefault<TSource>(this IEnumerable<TSource> source, TSource defaultValue) => source.DefaultIfEmpty(defaultValue).Min();
  413. /// <summary>
  414. /// 标准差
  415. /// </summary>
  416. /// <typeparam name="T"></typeparam>
  417. /// <param name="source"></param>
  418. /// <param name="selector"></param>
  419. /// <returns></returns>
  420. public static TResult StandardDeviation<T, TResult>(this IEnumerable<T> source, Func<T, TResult> selector) where TResult : IConvertible
  421. {
  422. return StandardDeviation(source.Select(t => selector(t).ConvertTo<double>())).ConvertTo<TResult>();
  423. }
  424. /// <summary>
  425. /// 标准差
  426. /// </summary>
  427. /// <typeparam name="T"></typeparam>
  428. /// <param name="source"></param>
  429. /// <returns></returns>
  430. public static T StandardDeviation<T>(this IEnumerable<T> source) where T : IConvertible
  431. {
  432. return StandardDeviation(source.Select(t => t.ConvertTo<double>())).ConvertTo<T>();
  433. }
  434. /// <summary>
  435. /// 标准差
  436. /// </summary>
  437. /// <param name="source"></param>
  438. /// <returns></returns>
  439. public static double StandardDeviation(this IEnumerable<double> source)
  440. {
  441. double result = 0;
  442. int count = source.Count();
  443. if (count > 1)
  444. {
  445. double avg = source.Average();
  446. double sum = source.Sum(d => (d - avg) * (d - avg));
  447. result = Math.Sqrt(sum / count);
  448. }
  449. return result;
  450. }
  451. /// <summary>
  452. /// 随机排序
  453. /// </summary>
  454. /// <typeparam name="T"></typeparam>
  455. /// <param name="source"></param>
  456. /// <returns></returns>
  457. public static IOrderedEnumerable<T> OrderByRandom<T>(this IEnumerable<T> source)
  458. {
  459. return source.OrderBy(_ => Guid.NewGuid());
  460. }
  461. /// <summary>
  462. /// 序列相等
  463. /// </summary>
  464. /// <typeparam name="T"></typeparam>
  465. /// <param name="first"></param>
  466. /// <param name="second"></param>
  467. /// <param name="condition"></param>
  468. /// <returns></returns>
  469. public static bool SequenceEqual<T>(this IEnumerable<T> first, IEnumerable<T> second, Func<T, T, bool> condition)
  470. {
  471. if (first is ICollection<T> source1 && second is ICollection<T> source2)
  472. {
  473. if (source1.Count != source2.Count)
  474. {
  475. return false;
  476. }
  477. if (source1 is IList<T> list1 && source2 is IList<T> list2)
  478. {
  479. int count = source1.Count;
  480. for (int index = 0; index < count; ++index)
  481. {
  482. if (!condition(list1[index], list2[index]))
  483. {
  484. return false;
  485. }
  486. }
  487. return true;
  488. }
  489. }
  490. using IEnumerator<T> enumerator1 = first.GetEnumerator();
  491. using IEnumerator<T> enumerator2 = second.GetEnumerator();
  492. while (enumerator1.MoveNext())
  493. {
  494. if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current))
  495. {
  496. return false;
  497. }
  498. }
  499. return !enumerator2.MoveNext();
  500. }
  501. /// <summary>
  502. /// 序列相等
  503. /// </summary>
  504. /// <typeparam name="T1"></typeparam>
  505. /// <typeparam name="T2"></typeparam>
  506. /// <param name="first"></param>
  507. /// <param name="second"></param>
  508. /// <param name="condition"></param>
  509. /// <returns></returns>
  510. public static bool SequenceEqual<T1, T2>(this IEnumerable<T1> first, IEnumerable<T2> second, Func<T1, T2, bool> condition)
  511. {
  512. if (first is ICollection<T1> source1 && second is ICollection<T2> source2)
  513. {
  514. if (source1.Count != source2.Count)
  515. {
  516. return false;
  517. }
  518. if (source1 is IList<T1> list1 && source2 is IList<T2> list2)
  519. {
  520. int count = source1.Count;
  521. for (int index = 0; index < count; ++index)
  522. {
  523. if (!condition(list1[index], list2[index]))
  524. {
  525. return false;
  526. }
  527. }
  528. return true;
  529. }
  530. }
  531. using IEnumerator<T1> enumerator1 = first.GetEnumerator();
  532. using IEnumerator<T2> enumerator2 = second.GetEnumerator();
  533. while (enumerator1.MoveNext())
  534. {
  535. if (!enumerator2.MoveNext() || !condition(enumerator1.Current, enumerator2.Current))
  536. {
  537. return false;
  538. }
  539. }
  540. return !enumerator2.MoveNext();
  541. }
  542. /// <summary>
  543. /// 对比两个集合哪些是新增的、删除的、修改的
  544. /// </summary>
  545. /// <typeparam name="T1"></typeparam>
  546. /// <typeparam name="T2"></typeparam>
  547. /// <param name="olds"></param>
  548. /// <param name="news"></param>
  549. /// <param name="key1Selector">对比因素属性</param>
  550. /// <param name="key2Selector">对比因素属性</param>
  551. /// <returns></returns>
  552. public static (List<T2> adds, List<T1> remove, List<T1> updates) CompareChanges<T1, T2>(this IEnumerable<T1> olds, IEnumerable<T2> news, Func<T1, object> key1Selector, Func<T2, object> key2Selector)
  553. {
  554. return (news.Where(c => olds.All(m => key1Selector(m) != key2Selector(c))).ToList(), olds.Where(m => news.All(c => key2Selector(c) != key1Selector(m))).ToList(), olds.Where(m => news.Any(c => key1Selector(m) == key2Selector(c))).ToList());
  555. }
  556. /// <summary>
  557. /// 对比两个集合哪些是新增的、删除的、修改的
  558. /// </summary>
  559. /// <typeparam name="T"></typeparam>
  560. /// <param name="olds"></param>
  561. /// <param name="news"></param>
  562. /// <param name="keySelector">对比因素属性</param>
  563. /// <returns></returns>
  564. public static (List<T> adds, List<T> remove, List<T> updates) CompareChanges<T>(this IEnumerable<T> olds, IEnumerable<T> news, Func<T, object> keySelector)
  565. {
  566. return (news.Where(c => olds.All(m => keySelector(m) != keySelector(c))).ToList(), olds.Where(m => news.All(c => keySelector(c) != keySelector(m))).ToList(), olds.Where(m => news.Any(c => keySelector(m) == keySelector(c))).ToList());
  567. }
  568. }
  569. }