EnumerableExtensions.cs 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. /// <summary>
  166. /// 尝试从字典中获取某项,如果该项不存在,则返回默认值。
  167. /// </summary>
  168. public static TValue GetOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dict, TKey key, TValue defaultValue = default!) =>
  169. dict.TryGetValue(key, out var result) ? result : defaultValue;
  170. /// <summary>
  171. /// 将值添加到字典的对应项的列表中。
  172. /// </summary>
  173. public static void AddIntoValueCollection<TKey, TValue>(this IDictionary<TKey, List<TValue>> dict, TKey key, TValue value) {
  174. if (dict.TryGetValue(key, out var collection)) {
  175. collection.Add(value);
  176. } else {
  177. dict.Add(key, [value]);
  178. }
  179. }
  180. #endregion
  181. /// <summary>
  182. /// 将 <see cref="FlagsAttribute"/> 枚举值解包为它包含的单一 Flag 成员的集合。
  183. /// 建议仅对没有极端值的正数枚举使用。
  184. /// </summary>
  185. /// <remarks>
  186. /// 会跳过:0、非 2 的幂的值、不在枚举值中的数、组合值、对应同一个枚举项的多个别名。
  187. /// </remarks>
  188. public static IEnumerable<T> Flags<T>(this T value) where T : struct, Enum {
  189. foreach (var element in Enum.GetValues(typeof(T)).Cast<T>().Distinct()) {
  190. var number = Convert.ToInt64(element);
  191. if (number == 0 || (number & (number - 1)) != 0) continue; // 跳过 0 和非 2 的幂的值
  192. if (value.HasFlag(element)) yield return element;
  193. }
  194. }
  195. /// <summary>
  196. /// 判断集合是否有且仅有一个元素。
  197. /// 在输入 null 时返回 false。
  198. /// </summary>
  199. public static bool IsSingle<T>([AllowNull] this IEnumerable<T> source) {
  200. if (source is null) return false;
  201. if (source is IList<T> list) return list.Count == 1;
  202. using var enumerator = source.GetEnumerator();
  203. if (!enumerator.MoveNext()) return false;
  204. if (enumerator.MoveNext()) return false;
  205. return true;
  206. }
  207. /// <summary>
  208. /// 对集合的每个元素执行指定操作。
  209. /// </summary>
  210. public static IEnumerable<T> ForAll<T>(this IEnumerable<T> source, Action<T> action) {
  211. foreach (T item in source) action(item);
  212. return source;
  213. }
  214. }