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.

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