EnumerableExtensions.cs 7.0 KB

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