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.

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