EnumerableExtensions.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. using System.Collections.Concurrent;
  2. namespace MeloongCore.Extensions;
  3. public static class EnumerableExtensions {
  4. #region Distinct
  5. /// <summary>
  6. /// 通过比较器进行去重。
  7. /// 该方法的效率较低,建议仅在小型列表上使用,或换用 DistinctBy。
  8. /// </summary>
  9. public static IEnumerable<T> Distinct<T>(this IList<T> list, Func<T, T, bool> comparer) {
  10. for (int i = 0; i < list.Count; i++) {
  11. if (!list.Skip(i + 1).Any(item => comparer(list[i], item))) yield return list[i];
  12. }
  13. }
  14. /// <summary>
  15. /// 按对象的指定值去重。
  16. /// </summary>
  17. public static IEnumerable<T> DistinctBy<T, TKey>(this IEnumerable<T> source, Func<T, TKey> selector) {
  18. var seen = new HashSet<TKey>();
  19. foreach (var element in source) {
  20. if (seen.Add(selector(element))) yield return element;
  21. }
  22. }
  23. /// <summary>
  24. /// 按对象的指定值去重。
  25. /// </summary>
  26. public static ParallelQuery<T> DistinctBy<T, TKey>(this ParallelQuery<T> source, Func<T, TKey> selector) {
  27. var seen = new ConcurrentDictionary<TKey, bool>();
  28. return source.Where(element => seen.TryAdd(selector(element), true));
  29. }
  30. #endregion
  31. #region MinBy / MaxBy
  32. /// <summary>
  33. /// 选择所有最大值对应的对象。
  34. /// 若没有元素则返回空列表。
  35. /// </summary>
  36. public static List<T> MaxByAll<T, C>(this IEnumerable<T> source, Func<T, C> selector) where C : IComparable<C> {
  37. var results = new List<T>();
  38. using var enumerator = source.GetEnumerator();
  39. if (!enumerator.MoveNext()) return results;
  40. T maxItem = enumerator.Current;
  41. C maxValue = selector(maxItem);
  42. results.Add(maxItem);
  43. while (enumerator.MoveNext()) {
  44. T currentItem = enumerator.Current;
  45. C currentValue = selector(currentItem);
  46. int comparisonResult = currentValue.CompareTo(maxValue);
  47. if (comparisonResult > 0) {
  48. maxValue = currentValue;
  49. results.Clear();
  50. results.Add(currentItem);
  51. } else if (comparisonResult == 0) {
  52. results.Add(currentItem);
  53. }
  54. }
  55. return results;
  56. }
  57. /// <summary>
  58. /// 选择所有最小值对应的对象。
  59. /// 若没有元素则返回空列表。
  60. /// </summary>
  61. public static List<T> MinByAll<T, C>(this IEnumerable<T> source, Func<T, C> selector) where C : IComparable<C> {
  62. var results = new List<T>();
  63. using var enumerator = source.GetEnumerator();
  64. if (!enumerator.MoveNext()) return results;
  65. T minItem = enumerator.Current;
  66. C minValue = selector(minItem);
  67. results.Add(minItem);
  68. while (enumerator.MoveNext()) {
  69. T currentItem = enumerator.Current;
  70. C currentValue = selector(currentItem);
  71. int comparisonResult = currentValue.CompareTo(minValue);
  72. if (comparisonResult < 0) {
  73. minValue = currentValue;
  74. results.Clear();
  75. results.Add(currentItem);
  76. } else if (comparisonResult == 0) {
  77. results.Add(currentItem);
  78. }
  79. }
  80. return results;
  81. }
  82. /// <summary>
  83. /// 选择最大值对应的对象。
  84. /// 若没有元素则返回 null。
  85. /// </summary>
  86. public static T? MaxBy<T, C>(this IEnumerable<T> source, Func<T, C> selector) where C : IComparable<C> {
  87. using var enumerator = source.GetEnumerator();
  88. if (!enumerator.MoveNext()) return default;
  89. T maxItem = enumerator.Current;
  90. C maxValue = selector(maxItem);
  91. while (enumerator.MoveNext()) {
  92. C value = selector(enumerator.Current);
  93. if (value.CompareTo(maxValue) <= 0) continue;
  94. maxItem = enumerator.Current;
  95. maxValue = value;
  96. }
  97. return maxItem;
  98. }
  99. /// <summary>
  100. /// 选择最小值对应的对象。
  101. /// 若没有元素则返回 null。
  102. /// </summary>
  103. public static T? MinBy<T, C>(this IEnumerable<T> List, Func<T, C> Selector) where C : IComparable<C> {
  104. using var enumerator = List.GetEnumerator();
  105. if (!enumerator.MoveNext()) { return default; }
  106. T minItem = enumerator.Current;
  107. C minValue = Selector(minItem);
  108. while (enumerator.MoveNext()) {
  109. C value = Selector(enumerator.Current);
  110. if (value.CompareTo(minValue) >= 0) { continue; }
  111. minItem = enumerator.Current;
  112. minValue = value;
  113. }
  114. return minItem;
  115. }
  116. #endregion
  117. #region Join
  118. /// <summary>
  119. /// 用指定的分割符将集合连接为字符串。
  120. /// </summary>
  121. public static string Join(this IEnumerable list, char split) {
  122. var builder = new StringBuilder();
  123. bool isFirst = true;
  124. foreach (var element in list) {
  125. if (isFirst)
  126. isFirst = false;
  127. else
  128. builder.Append(split);
  129. if (element is not null) builder.Append(element.ToString());
  130. }
  131. return builder.ToString();
  132. }
  133. /// <summary>
  134. /// 用指定的分割符将集合连接为字符串。
  135. /// </summary>
  136. public static string Join(this IEnumerable list, string split) {
  137. if (split.IsSingle()) return list.Join(split[0]);
  138. var builder = new StringBuilder();
  139. bool isFirst = true;
  140. foreach (var element in list) {
  141. if (isFirst)
  142. isFirst = false;
  143. else
  144. builder.Append(split);
  145. if (element is not null) builder.Append(element.ToString());
  146. }
  147. return builder.ToString();
  148. }
  149. #endregion
  150. /// <summary>
  151. /// 判断集合是否有且仅有一个元素。
  152. /// 在输入 null 时返回 false。
  153. /// </summary>
  154. public static bool IsSingle<T>([AllowNull] this IEnumerable<T> source) {
  155. if (source is null) return false;
  156. if (source is IList<T> list) return list.Count == 1;
  157. using var enumerator = source.GetEnumerator();
  158. if (!enumerator.MoveNext()) return false;
  159. if (enumerator.MoveNext()) return false;
  160. return true;
  161. }
  162. /// <summary>
  163. /// 对集合的每个元素执行指定操作。
  164. /// </summary>
  165. public static IEnumerable<T> ForAll<T>(this IEnumerable<T> source, Action<T> action) {
  166. foreach (T item in source) action(item);
  167. return source;
  168. }
  169. }