You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

207 lines
8.4 KiB

  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Threading.Tasks;
  7. namespace IPA.Utilities
  8. {
  9. /// <summary>
  10. /// Extensions for <see cref="IEnumerable{T}"/> that don't currently exist in <c>System.Linq</c>.
  11. /// </summary>
  12. public static class EnumerableExtensions
  13. {
  14. /// <summary>
  15. /// Adds a value to the beginning of the sequence.
  16. /// </summary>
  17. /// <typeparam name="T">the type of the elements of <paramref name="seq"/></typeparam>
  18. /// <param name="seq">a sequence of values</param>
  19. /// <param name="prep">the value to prepend to <paramref name="seq"/></param>
  20. /// <returns>a new sequence beginning with <paramref name="prep"/></returns>
  21. public static IEnumerable<T> Prepend<T>(this IEnumerable<T> seq, T prep)
  22. => new PrependEnumerable<T>(seq, prep);
  23. private sealed class PrependEnumerable<T> : IEnumerable<T>
  24. {
  25. private readonly IEnumerable<T> rest;
  26. private readonly T first;
  27. public PrependEnumerable(IEnumerable<T> rest, T first)
  28. {
  29. this.rest = rest;
  30. this.first = first;
  31. }
  32. public PrependEnumerator GetEnumerator() => new PrependEnumerator(this);
  33. public struct PrependEnumerator : IEnumerator<T>
  34. {
  35. private readonly IEnumerator<T> restEnum;
  36. private readonly PrependEnumerable<T> enumerable;
  37. private int state;
  38. internal PrependEnumerator(PrependEnumerable<T> enumerable)
  39. {
  40. this.enumerable = enumerable;
  41. restEnum = enumerable.rest.GetEnumerator();
  42. state = 0;
  43. Current = default;
  44. }
  45. public T Current { get; private set; }
  46. object IEnumerator.Current => Current;
  47. public void Dispose() => restEnum.Dispose();
  48. public bool MoveNext()
  49. {
  50. switch (state)
  51. {
  52. case 0:
  53. Current = enumerable.first;
  54. state++;
  55. return true;
  56. case 1:
  57. if (!restEnum.MoveNext())
  58. {
  59. state = 2;
  60. return false;
  61. }
  62. else
  63. Current = restEnum.Current;
  64. return true;
  65. case 2:
  66. default:
  67. return false;
  68. }
  69. }
  70. public void Reset()
  71. {
  72. restEnum.Reset();
  73. state = 0;
  74. }
  75. }
  76. IEnumerator<T> IEnumerable<T>.GetEnumerator() => new PrependEnumerator(this);
  77. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  78. }
  79. /// <summary>
  80. /// Adds a value to the end of the sequence.
  81. /// </summary>
  82. /// <typeparam name="T">the type of the elements of <paramref name="seq"/></typeparam>
  83. /// <param name="seq">a sequence of values</param>
  84. /// <param name="app">the value to append to <paramref name="seq"/></param>
  85. /// <returns>a new sequence ending with <paramref name="app"/></returns>
  86. public static IEnumerable<T> Append<T>(this IEnumerable<T> seq, T app)
  87. => new AppendEnumerable<T>(seq, app);
  88. private sealed class AppendEnumerable<T> : IEnumerable<T>
  89. {
  90. private readonly IEnumerable<T> rest;
  91. private readonly T last;
  92. public AppendEnumerable(IEnumerable<T> rest, T last)
  93. {
  94. this.rest = rest;
  95. this.last = last;
  96. }
  97. public AppendEnumerator GetEnumerator() => new AppendEnumerator(this);
  98. public struct AppendEnumerator : IEnumerator<T>
  99. {
  100. private readonly IEnumerator<T> restEnum;
  101. private readonly AppendEnumerable<T> enumerable;
  102. private int state;
  103. internal AppendEnumerator(AppendEnumerable<T> enumerable)
  104. {
  105. this.enumerable = enumerable;
  106. restEnum = enumerable.rest.GetEnumerator();
  107. state = 0;
  108. Current = default;
  109. }
  110. public T Current { get; private set; }
  111. object IEnumerator.Current => Current;
  112. public void Dispose() => restEnum.Dispose();
  113. public bool MoveNext()
  114. {
  115. switch (state)
  116. {
  117. case 0:
  118. if (!restEnum.MoveNext())
  119. {
  120. state = 1;
  121. goto case 1;
  122. }
  123. else
  124. Current = restEnum.Current;
  125. return true;
  126. case 1:
  127. Current = enumerable.last;
  128. state++;
  129. return true;
  130. case 2:
  131. default:
  132. return false;
  133. }
  134. }
  135. public void Reset()
  136. {
  137. restEnum.Reset();
  138. state = 0;
  139. }
  140. }
  141. IEnumerator<T> IEnumerable<T>.GetEnumerator() => GetEnumerator();
  142. IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
  143. }
  144. /// <summary>
  145. /// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration.
  146. /// </summary>
  147. /// <typeparam name="T">the type of the enumeration</typeparam>
  148. /// <param name="self">the enumeration to filter</param>
  149. /// <returns>a filtered enumerable</returns>
  150. public static IEnumerable<T> NonNull<T>(this IEnumerable<T> self) where T : class
  151. => self.Where(o => o != null);
  152. /// <summary>
  153. /// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter.
  154. /// </summary>
  155. /// <typeparam name="T">the type of the enumeration</typeparam>
  156. /// <typeparam name="U">the type to compare to null</typeparam>
  157. /// <param name="self">the enumeration to filter</param>
  158. /// <param name="pred">the predicate to select for filtering</param>
  159. /// <returns>a filtered enumerable</returns>
  160. public static IEnumerable<T> NonNull<T, U>(this IEnumerable<T> self, Func<T, U> pred) where U : class
  161. => self.Where(o => pred(o) != null);
  162. /// <summary>
  163. /// LINQ-style extension method that filters <see langword="null"/> elements from an enumeration of nullable types.
  164. /// </summary>
  165. /// <typeparam name="T">the underlying type of the nullable enumeration</typeparam>
  166. /// <param name="self">the enumeration to filter</param>
  167. /// <returns>a filtered enumerable</returns>
  168. public static IEnumerable<T> NonNull<T>(this IEnumerable<T?> self) where T : struct
  169. => self.Where(o => o != null).Select(o => o.Value);
  170. /// <summary>
  171. /// LINQ-style extension method that filters <see langword="null"/> elements out of an enumeration based on a converter to a nullable type.
  172. /// </summary>
  173. /// <typeparam name="T">the type of the enumeration</typeparam>
  174. /// <typeparam name="U">the type of the predicate's resulting nullable</typeparam>
  175. /// <param name="self">the enumeration to filter</param>
  176. /// <param name="pred">the predicate to select for filtering</param>
  177. /// <returns>a filtered enumerable</returns>
  178. public static IEnumerable<T> NonNull<T, U>(this IEnumerable<T> self, Func<T, U?> pred) where U : struct
  179. => self.Where(o => pred(o) != null);
  180. }
  181. }