EnumerableExtensions.cs 7.2 KB

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