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.

208 lines
8.3 KiB

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