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.

413 lines
11 KiB

  1. #nullable enable
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Collections.Immutable;
  5. using System.Linq;
  6. namespace ICSharpCode.Decompiler.Util
  7. {
  8. static class CollectionExtensions
  9. {
  10. public static void Deconstruct<K, V>(this KeyValuePair<K, V> pair, out K key, out V value)
  11. {
  12. key = pair.Key;
  13. value = pair.Value;
  14. }
  15. #if !NETCORE
  16. public static IEnumerable<(A, B)> Zip<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
  17. {
  18. return input1.Zip(input2, (a, b) => (a, b));
  19. }
  20. #endif
  21. public static IEnumerable<(A?, B?)> ZipLongest<A, B>(this IEnumerable<A> input1, IEnumerable<B> input2)
  22. {
  23. using (var it1 = input1.GetEnumerator())
  24. {
  25. using (var it2 = input2.GetEnumerator())
  26. {
  27. bool hasElements1 = true;
  28. bool hasElements2 = true;
  29. while (true)
  30. {
  31. if (hasElements1)
  32. hasElements1 = it1.MoveNext();
  33. if (hasElements2)
  34. hasElements2 = it2.MoveNext();
  35. if (!(hasElements1 || hasElements2))
  36. break;
  37. yield return ((hasElements1 ? it1.Current : default), (hasElements2 ? it2.Current : default));
  38. }
  39. }
  40. }
  41. }
  42. public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset, int length)
  43. {
  44. for (int i = offset; i < offset + length; i++)
  45. {
  46. yield return input[i];
  47. }
  48. }
  49. public static IEnumerable<T> Slice<T>(this IReadOnlyList<T> input, int offset)
  50. {
  51. int length = input.Count;
  52. for (int i = offset; i < length; i++)
  53. {
  54. yield return input[i];
  55. }
  56. }
  57. #if !NETCORE
  58. public static HashSet<T> ToHashSet<T>(this IEnumerable<T> input)
  59. {
  60. return new HashSet<T>(input);
  61. }
  62. #endif
  63. public static IEnumerable<T> SkipLast<T>(this IReadOnlyCollection<T> input, int count)
  64. {
  65. return input.Take(input.Count - count);
  66. }
  67. public static IEnumerable<T> TakeLast<T>(this IReadOnlyCollection<T> input, int count)
  68. {
  69. return input.Skip(input.Count - count);
  70. }
  71. public static T? PopOrDefault<T>(this Stack<T> stack)
  72. {
  73. if (stack.Count == 0)
  74. return default(T);
  75. return stack.Pop();
  76. }
  77. public static T? PeekOrDefault<T>(this Stack<T> stack)
  78. {
  79. if (stack.Count == 0)
  80. return default(T);
  81. return stack.Peek();
  82. }
  83. public static int MaxOrDefault<T>(this IEnumerable<T> input, Func<T, int> selector, int defaultValue = 0)
  84. {
  85. int max = defaultValue;
  86. foreach (var element in input)
  87. {
  88. int value = selector(element);
  89. if (value > max)
  90. max = value;
  91. }
  92. return max;
  93. }
  94. public static int IndexOf<T>(this IReadOnlyList<T> collection, T value)
  95. {
  96. var comparer = EqualityComparer<T>.Default;
  97. int index = 0;
  98. foreach (T item in collection)
  99. {
  100. if (comparer.Equals(item, value))
  101. {
  102. return index;
  103. }
  104. index++;
  105. }
  106. return -1;
  107. }
  108. public static void AddRange<T>(this ICollection<T> collection, IEnumerable<T> input)
  109. {
  110. foreach (T item in input)
  111. collection.Add(item);
  112. }
  113. /// <summary>
  114. /// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
  115. /// use of the input collection's known size.
  116. /// </summary>
  117. public static U[] SelectArray<T, U>(this ICollection<T> collection, Func<T, U> func)
  118. {
  119. U[] result = new U[collection.Count];
  120. int index = 0;
  121. foreach (var element in collection)
  122. {
  123. result[index++] = func(element);
  124. }
  125. return result;
  126. }
  127. /// <summary>
  128. /// Equivalent to <code>collection.Select(func).ToImmutableArray()</code>, but more efficient as it makes
  129. /// use of the input collection's known size.
  130. /// </summary>
  131. public static ImmutableArray<U> SelectImmutableArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func)
  132. {
  133. var builder = ImmutableArray.CreateBuilder<U>(collection.Count);
  134. foreach (var element in collection)
  135. {
  136. builder.Add(func(element));
  137. }
  138. return builder.MoveToImmutable();
  139. }
  140. /// <summary>
  141. /// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
  142. /// use of the input collection's known size.
  143. /// </summary>
  144. public static U[] SelectReadOnlyArray<T, U>(this IReadOnlyCollection<T> collection, Func<T, U> func)
  145. {
  146. U[] result = new U[collection.Count];
  147. int index = 0;
  148. foreach (var element in collection)
  149. {
  150. result[index++] = func(element);
  151. }
  152. return result;
  153. }
  154. /// <summary>
  155. /// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
  156. /// use of the input collection's known size.
  157. /// </summary>
  158. public static U[] SelectArray<T, U>(this List<T> collection, Func<T, U> func)
  159. {
  160. U[] result = new U[collection.Count];
  161. int index = 0;
  162. foreach (var element in collection)
  163. {
  164. result[index++] = func(element);
  165. }
  166. return result;
  167. }
  168. /// <summary>
  169. /// Equivalent to <code>collection.Select(func).ToArray()</code>, but more efficient as it makes
  170. /// use of the input collection's known size.
  171. /// </summary>
  172. public static U[] SelectArray<T, U>(this T[] collection, Func<T, U> func)
  173. {
  174. U[] result = new U[collection.Length];
  175. int index = 0;
  176. foreach (var element in collection)
  177. {
  178. result[index++] = func(element);
  179. }
  180. return result;
  181. }
  182. /// <summary>
  183. /// Equivalent to <code>collection.Select(func).ToList()</code>, but more efficient as it makes
  184. /// use of the input collection's known size.
  185. /// </summary>
  186. public static List<U> SelectList<T, U>(this ICollection<T> collection, Func<T, U> func)
  187. {
  188. List<U> result = new List<U>(collection.Count);
  189. foreach (var element in collection)
  190. {
  191. result.Add(func(element));
  192. }
  193. return result;
  194. }
  195. public static IEnumerable<U> SelectWithIndex<T, U>(this IEnumerable<T> source, Func<int, T, U> func)
  196. {
  197. int index = 0;
  198. foreach (var element in source)
  199. yield return func(index++, element);
  200. }
  201. public static IEnumerable<(int, T)> WithIndex<T>(this ICollection<T> source)
  202. {
  203. int index = 0;
  204. foreach (var item in source)
  205. {
  206. yield return (index, item);
  207. index++;
  208. }
  209. }
  210. /// <summary>
  211. /// The merge step of merge sort.
  212. /// </summary>
  213. public static IEnumerable<T> Merge<T>(this IEnumerable<T> input1, IEnumerable<T> input2, Comparison<T> comparison)
  214. {
  215. using (var enumA = input1.GetEnumerator())
  216. using (var enumB = input2.GetEnumerator())
  217. {
  218. bool moreA = enumA.MoveNext();
  219. bool moreB = enumB.MoveNext();
  220. while (moreA && moreB)
  221. {
  222. if (comparison(enumA.Current, enumB.Current) <= 0)
  223. {
  224. yield return enumA.Current;
  225. moreA = enumA.MoveNext();
  226. }
  227. else
  228. {
  229. yield return enumB.Current;
  230. moreB = enumB.MoveNext();
  231. }
  232. }
  233. while (moreA)
  234. {
  235. yield return enumA.Current;
  236. moreA = enumA.MoveNext();
  237. }
  238. while (moreB)
  239. {
  240. yield return enumB.Current;
  241. moreB = enumB.MoveNext();
  242. }
  243. }
  244. }
  245. /// <summary>
  246. /// Returns the minimum element.
  247. /// </summary>
  248. /// <exception cref="InvalidOperationException">The input sequence is empty</exception>
  249. public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K>
  250. {
  251. return source.MinBy(keySelector, Comparer<K>.Default);
  252. }
  253. /// <summary>
  254. /// Returns the minimum element.
  255. /// </summary>
  256. /// <exception cref="InvalidOperationException">The input sequence is empty</exception>
  257. public static T MinBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer)
  258. {
  259. if (source == null)
  260. throw new ArgumentNullException(nameof(source));
  261. if (keySelector == null)
  262. throw new ArgumentNullException(nameof(keySelector));
  263. if (keyComparer == null)
  264. keyComparer = Comparer<K>.Default;
  265. using (var enumerator = source.GetEnumerator())
  266. {
  267. if (!enumerator.MoveNext())
  268. throw new InvalidOperationException("Sequence contains no elements");
  269. T minElement = enumerator.Current;
  270. K minKey = keySelector(minElement);
  271. while (enumerator.MoveNext())
  272. {
  273. T element = enumerator.Current;
  274. K key = keySelector(element);
  275. if (keyComparer.Compare(key, minKey) < 0)
  276. {
  277. minElement = element;
  278. minKey = key;
  279. }
  280. }
  281. return minElement;
  282. }
  283. }
  284. /// <summary>
  285. /// Returns the maximum element.
  286. /// </summary>
  287. /// <exception cref="InvalidOperationException">The input sequence is empty</exception>
  288. public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector) where K : IComparable<K>
  289. {
  290. return source.MaxBy(keySelector, Comparer<K>.Default);
  291. }
  292. /// <summary>
  293. /// Returns the maximum element.
  294. /// </summary>
  295. /// <exception cref="InvalidOperationException">The input sequence is empty</exception>
  296. public static T MaxBy<T, K>(this IEnumerable<T> source, Func<T, K> keySelector, IComparer<K>? keyComparer)
  297. {
  298. if (source == null)
  299. throw new ArgumentNullException(nameof(source));
  300. if (keySelector == null)
  301. throw new ArgumentNullException(nameof(keySelector));
  302. if (keyComparer == null)
  303. keyComparer = Comparer<K>.Default;
  304. using (var enumerator = source.GetEnumerator())
  305. {
  306. if (!enumerator.MoveNext())
  307. throw new InvalidOperationException("Sequence contains no elements");
  308. T maxElement = enumerator.Current;
  309. K maxKey = keySelector(maxElement);
  310. while (enumerator.MoveNext())
  311. {
  312. T element = enumerator.Current;
  313. K key = keySelector(element);
  314. if (keyComparer.Compare(key, maxKey) > 0)
  315. {
  316. maxElement = element;
  317. maxKey = key;
  318. }
  319. }
  320. return maxElement;
  321. }
  322. }
  323. public static void RemoveLast<T>(this IList<T> list)
  324. {
  325. if (list == null)
  326. throw new ArgumentNullException(nameof(list));
  327. list.RemoveAt(list.Count - 1);
  328. }
  329. public static T? OnlyOrDefault<T>(this IEnumerable<T> source, Func<T, bool> predicate) => OnlyOrDefault(source.Where(predicate));
  330. public static T? OnlyOrDefault<T>(this IEnumerable<T> source)
  331. {
  332. bool any = false;
  333. T? first = default;
  334. foreach (var t in source)
  335. {
  336. if (any)
  337. return default(T);
  338. first = t;
  339. any = true;
  340. }
  341. return first;
  342. }
  343. #if !NETCORE
  344. public static int EnsureCapacity<T>(this List<T> list, int capacity)
  345. {
  346. if (capacity < 0)
  347. throw new ArgumentOutOfRangeException(nameof(capacity));
  348. if (list.Capacity < capacity)
  349. {
  350. const int DefaultCapacity = 4;
  351. const int MaxLength = 0X7FFFFFC7;
  352. int newcapacity = list.Capacity == 0 ? DefaultCapacity : 2 * list.Capacity;
  353. if ((uint)newcapacity > MaxLength)
  354. newcapacity = MaxLength;
  355. if (newcapacity < capacity)
  356. newcapacity = capacity;
  357. list.Capacity = newcapacity;
  358. }
  359. return list.Capacity;
  360. }
  361. #endif
  362. #region Aliases/shortcuts for Enumerable extension methods
  363. public static bool Any<T>(this ICollection<T> list) => list.Count > 0;
  364. public static bool Any<T>(this T[] array, Predicate<T> match) => Array.Exists(array, match);
  365. public static bool Any<T>(this List<T> list, Predicate<T> match) => list.Exists(match);
  366. public static bool All<T>(this T[] array, Predicate<T> match) => Array.TrueForAll(array, match);
  367. public static bool All<T>(this List<T> list, Predicate<T> match) => list.TrueForAll(match);
  368. public static T? FirstOrDefault<T>(this T[] array, Predicate<T> predicate) => Array.Find(array, predicate);
  369. public static T? FirstOrDefault<T>(this List<T> list, Predicate<T> predicate) => list.Find(predicate);
  370. public static T Last<T>(this IList<T> list) => list[list.Count - 1];
  371. #endregion
  372. }
  373. }