/Signum.Utilities/EnumerableExtensions.cs

http://signum.codeplex.com · C# · 1129 lines · 928 code · 198 blank · 3 comment · 141 complexity · 59e22123c19c23b5e1cd91174cc9420a MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.Linq.Expressions;
  6. using System.Reflection;
  7. using System.IO;
  8. using System.Threading;
  9. using System.Globalization;
  10. using Signum.Utilities.Synchronization;
  11. using Signum.Utilities.DataStructures;
  12. using Signum.Utilities.Reflection;
  13. using System.Collections.ObjectModel;
  14. using System.Diagnostics;
  15. using System.Data;
  16. using System.Text.RegularExpressions;
  17. using Signum.Utilities.Properties;
  18. using System.Collections;
  19. using Signum.Utilities.ExpressionTrees;
  20. namespace Signum.Utilities
  21. {
  22. public static class EnumerableUniqueExtensions
  23. {
  24. class UniqueExExpander : IMethodExpander
  25. {
  26. static MethodInfo miWhereE = ReflectionTools.GetMethodInfo(() => Enumerable.Where<int>(null, a => false)).GetGenericMethodDefinition();
  27. static MethodInfo miWhereQ = ReflectionTools.GetMethodInfo(() => Queryable.Where<int>(null, a => false)).GetGenericMethodDefinition();
  28. public Expression Expand(Expression instance, Expression[] arguments, MethodInfo mi)
  29. {
  30. bool query = mi.GetParameters()[0].ParameterType.IsInstantiationOf(typeof(IQueryable<>));
  31. var whereMi = (query ? miWhereQ : miWhereE).MakeGenericMethod(mi.GetGenericArguments());
  32. var whereExpr = Expression.Call(whereMi, arguments[0], arguments[1]);
  33. var uniqueMi = mi.DeclaringType.GetMethods().SingleEx(m => m.Name == mi.Name && m.IsGenericMethod && m.GetParameters().Length == (mi.GetParameters().Length - 1));
  34. return Expression.Call(uniqueMi.MakeGenericMethod(mi.GetGenericArguments()), whereExpr);
  35. }
  36. }
  37. [MethodExpander(typeof(UniqueExExpander))]
  38. public static T SingleEx<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
  39. {
  40. if (collection == null)
  41. throw new ArgumentNullException("collection");
  42. if (predicate == null)
  43. throw new ArgumentNullException("predicate");
  44. T result = default(T);
  45. bool found = false;
  46. foreach (T item in collection)
  47. {
  48. if (predicate(item))
  49. {
  50. if (found)
  51. throw new InvalidOperationException("Sequence contains more than one {0}".Formato(typeof(T).TypeName()));
  52. result = item;
  53. found = true;
  54. }
  55. }
  56. if (found)
  57. return result;
  58. throw new InvalidOperationException("Sequence contains no {0}".Formato(typeof(T).TypeName()));
  59. }
  60. [MethodExpander(typeof(UniqueExExpander))]
  61. public static T SingleEx<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
  62. {
  63. return query.Where(predicate).SingleEx();
  64. }
  65. public static T SingleEx<T>(this IEnumerable<T> collection)
  66. {
  67. if (collection == null)
  68. throw new ArgumentNullException("collection");
  69. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  70. {
  71. if (!enumerator.MoveNext())
  72. throw new InvalidOperationException("Sequence contains no {0}".Formato(typeof(T).TypeName()));
  73. T current = enumerator.Current;
  74. if (!enumerator.MoveNext())
  75. return current;
  76. }
  77. throw new InvalidOperationException("Sequence contains more than one {0}".Formato(typeof(T).TypeName()));
  78. }
  79. public static T SingleEx<T>(this IEnumerable<T> collection, Func<string> error)
  80. {
  81. return collection.SingleEx(error, error);
  82. }
  83. public static T SingleEx<T>(this IEnumerable<T> collection, Func<string> errorZero, Func<string> errorMoreThanOne)
  84. {
  85. if (collection == null)
  86. throw new ArgumentNullException("collection");
  87. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  88. {
  89. if (!enumerator.MoveNext())
  90. throw new InvalidOperationException(errorZero());
  91. T current = enumerator.Current;
  92. if (!enumerator.MoveNext())
  93. return current;
  94. }
  95. throw new InvalidOperationException(errorMoreThanOne());
  96. }
  97. [MethodExpander(typeof(UniqueExExpander))]
  98. public static T SingleOrDefaultEx<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
  99. {
  100. if (collection == null)
  101. throw new ArgumentNullException("collection");
  102. if (predicate == null)
  103. throw new ArgumentNullException("predicate");
  104. T result = default(T);
  105. bool found = false;
  106. foreach (T item in collection)
  107. {
  108. if (predicate(item))
  109. {
  110. if (found)
  111. throw new InvalidOperationException("Sequence contains more than one {0}".Formato(typeof(T).TypeName()));
  112. result = item;
  113. found = true;
  114. }
  115. }
  116. return result;
  117. }
  118. [MethodExpander(typeof(UniqueExExpander))]
  119. public static T SingleOrDefaultEx<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
  120. {
  121. return query.Where(predicate).SingleOrDefaultEx();
  122. }
  123. public static T SingleOrDefaultEx<T>(this IEnumerable<T> collection)
  124. {
  125. if (collection == null)
  126. throw new ArgumentNullException("collection");
  127. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  128. {
  129. if (!enumerator.MoveNext())
  130. return default(T);
  131. T current = enumerator.Current;
  132. if (!enumerator.MoveNext())
  133. return current;
  134. }
  135. throw new InvalidOperationException("Sequence contains more than one {0}".Formato(typeof(T).TypeName()));
  136. }
  137. public static T SingleOrDefaultEx<T>(this IEnumerable<T> collection, Func<string> errorMorethanOne)
  138. {
  139. if (collection == null)
  140. throw new ArgumentNullException("collection");
  141. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  142. {
  143. if (!enumerator.MoveNext())
  144. return default(T);
  145. T current = enumerator.Current;
  146. if (!enumerator.MoveNext())
  147. return current;
  148. }
  149. throw new InvalidOperationException(errorMorethanOne());
  150. }
  151. [MethodExpander(typeof(UniqueExExpander))]
  152. public static T FirstEx<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
  153. {
  154. if (collection == null)
  155. throw new ArgumentNullException("collection");
  156. if (predicate == null)
  157. throw new ArgumentNullException("predicate");
  158. foreach (T item in collection)
  159. {
  160. if (predicate(item))
  161. return item;
  162. }
  163. throw new InvalidOperationException("Sequence contains no {0}".Formato(typeof(T).TypeName()));
  164. }
  165. [MethodExpander(typeof(UniqueExExpander))]
  166. public static T FirstEx<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
  167. {
  168. return query.Where(predicate).FirstEx();
  169. }
  170. public static T FirstEx<T>(this IEnumerable<T> collection)
  171. {
  172. if (collection == null)
  173. throw new ArgumentNullException("collection");
  174. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  175. {
  176. if (!enumerator.MoveNext())
  177. throw new InvalidOperationException("Sequence contains no {0}".Formato(typeof(T).TypeName()));
  178. return enumerator.Current;
  179. }
  180. }
  181. public static T FirstEx<T>(this IEnumerable<T> collection, Func<string> errorZero)
  182. {
  183. if (collection == null)
  184. throw new ArgumentNullException("collection");
  185. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  186. {
  187. if (!enumerator.MoveNext())
  188. throw new InvalidOperationException(errorZero());
  189. return enumerator.Current;
  190. }
  191. }
  192. [MethodExpander(typeof(UniqueExExpander))]
  193. public static T SingleOrManyEx<T>(this IEnumerable<T> collection, Func<T, bool> predicate)
  194. {
  195. return collection.Where(predicate).FirstEx();
  196. }
  197. [MethodExpander(typeof(UniqueExExpander))]
  198. public static T SingleOrManyEx<T>(this IQueryable<T> query, Expression<Func<T, bool>> predicate)
  199. {
  200. return query.Where(predicate).FirstEx();
  201. }
  202. public static T SingleOrManyEx<T>(this IEnumerable<T> collection)
  203. {
  204. if (collection == null)
  205. throw new ArgumentNullException("collection");
  206. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  207. {
  208. if (!enumerator.MoveNext())
  209. throw new InvalidOperationException("Sequence contains no {0}".Formato(typeof(T).TypeName()));
  210. T current = enumerator.Current;
  211. if (enumerator.MoveNext())
  212. return default(T);
  213. return current;
  214. }
  215. }
  216. public static T SingleOrManyEx<T>(this IEnumerable<T> collection, Func<string> errorZero)
  217. {
  218. if (collection == null)
  219. throw new ArgumentNullException("collection");
  220. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  221. {
  222. if (!enumerator.MoveNext())
  223. throw new InvalidOperationException(errorZero());
  224. T current = enumerator.Current;
  225. if (enumerator.MoveNext())
  226. return default(T);
  227. return current;
  228. }
  229. }
  230. //Throws exception if 0, returns if one, returns default if many
  231. public static T SingleOrMany<T>(this IEnumerable<T> collection)
  232. {
  233. if (collection == null)
  234. throw new ArgumentNullException("collection");
  235. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  236. {
  237. if (!enumerator.MoveNext())
  238. throw new InvalidOperationException("The collection has no elements");
  239. T current = enumerator.Current;
  240. if (enumerator.MoveNext())
  241. return default(T);
  242. return current;
  243. }
  244. }
  245. //returns default if 0 or many, returns if one
  246. public static T Only<T>(this IEnumerable<T> collection)
  247. {
  248. if (collection == null)
  249. throw new ArgumentNullException("collection");
  250. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  251. {
  252. if (!enumerator.MoveNext())
  253. return default(T);
  254. T current = enumerator.Current;
  255. if (enumerator.MoveNext())
  256. return default(T);
  257. return current;
  258. }
  259. }
  260. }
  261. public static class EnumerableExtensions
  262. {
  263. public static bool IsEmpty<T>(this IEnumerable<T> collection)
  264. {
  265. foreach (var item in collection)
  266. return false;
  267. return true;
  268. }
  269. public static bool IsNullOrEmpty<T>(this IEnumerable<T> collection)
  270. {
  271. return collection == null || collection.IsEmpty();
  272. }
  273. public static IEnumerable<T> NotNull<T>(this IEnumerable<T> collection) where T : class
  274. {
  275. foreach (var item in collection)
  276. {
  277. if (item != null)
  278. yield return item;
  279. }
  280. }
  281. public static IEnumerable<T> NotNull<T>(this IEnumerable<T?> collection) where T : struct
  282. {
  283. foreach (var item in collection)
  284. {
  285. if (item.HasValue)
  286. yield return item.Value;
  287. }
  288. }
  289. public static IEnumerable<T> And<T>(this IEnumerable<T> collection, T newItem)
  290. {
  291. foreach (var item in collection)
  292. yield return item;
  293. yield return newItem;
  294. }
  295. public static IEnumerable<T> PreAnd<T>(this IEnumerable<T> collection, T newItem)
  296. {
  297. yield return newItem;
  298. foreach (var item in collection)
  299. yield return item;
  300. }
  301. public static int IndexOf<T>(this IEnumerable<T> collection, T item)
  302. {
  303. int i = 0;
  304. foreach (var val in collection)
  305. {
  306. if (EqualityComparer<T>.Default.Equals(item, val))
  307. return i;
  308. i++;
  309. }
  310. return -1;
  311. }
  312. public static int IndexOf<T>(this IEnumerable<T> collection, Func<T, bool> condition)
  313. {
  314. int i = 0;
  315. foreach (var val in collection)
  316. {
  317. if (condition(val))
  318. return i;
  319. i++;
  320. }
  321. return -1;
  322. }
  323. public static string ToString<T>(this IEnumerable<T> collection, string separator)
  324. {
  325. StringBuilder sb = new StringBuilder();
  326. foreach (var item in collection)
  327. {
  328. sb.Append(item.ToString());
  329. sb.Append(separator);
  330. }
  331. return sb.ToString(0, Math.Max(0, sb.Length - separator.Length)); // Remove at the end is faster
  332. }
  333. public static string ToString<T>(this IEnumerable<T> collection, Func<T, string> toString, string separator)
  334. {
  335. StringBuilder sb = new StringBuilder();
  336. foreach (var item in collection)
  337. {
  338. sb.Append(toString(item));
  339. sb.Append(separator);
  340. }
  341. return sb.ToString(0, Math.Max(0, sb.Length - separator.Length)); // Remove at the end is faster
  342. }
  343. public static string CommaAnd<T>(this IEnumerable<T> collection)
  344. {
  345. return CommaString(collection.Select(a => a.ToString()).ToArray(), Resources.And);
  346. }
  347. public static string CommaAnd<T>(this IEnumerable<T> collection, Func<T, string> toString)
  348. {
  349. return CommaString(collection.Select(toString).ToArray(), Resources.And);
  350. }
  351. public static string CommaOr<T>(this IEnumerable<T> collection)
  352. {
  353. return CommaString(collection.Select(a => a.ToString()).ToArray(), Resources.Or);
  354. }
  355. public static string CommaOr<T>(this IEnumerable<T> collection, Func<T, string> toString)
  356. {
  357. return CommaString(collection.Select(toString).ToArray(), Resources.Or);
  358. }
  359. public static string Comma<T>(this IEnumerable<T> collection, string lastSeparator)
  360. {
  361. return CommaString(collection.Select(a => a.ToString()).ToArray(), lastSeparator);
  362. }
  363. public static string Comma<T>(this IEnumerable<T> collection, Func<T, string> toString, string lastSeparator)
  364. {
  365. return CommaString(collection.Select(toString).ToArray(), lastSeparator);
  366. }
  367. static string CommaString(this string[] values, string lastSeparator)
  368. {
  369. if (values.Length == 0)
  370. return "";
  371. StringBuilder sb = new StringBuilder();
  372. sb.Append(values[0]);
  373. for (int i = 1; i < values.Length - 1; i++)
  374. {
  375. sb.Append(", ");
  376. sb.Append(values[i]);
  377. }
  378. if (values.Length > 1)
  379. {
  380. sb.Append(lastSeparator);
  381. sb.Append(values[values.Length - 1]);
  382. }
  383. return sb.ToString();
  384. }
  385. public static void ToConsole<T>(this IEnumerable<T> collection)
  386. {
  387. ToConsole(collection, a => a.ToString());
  388. }
  389. public static void ToConsole<T>(this IEnumerable<T> collection, Func<T, string> toString)
  390. {
  391. foreach (var item in collection)
  392. Console.WriteLine(toString(item));
  393. }
  394. public static void ToFile(this IEnumerable<string> collection, string fileName)
  395. {
  396. using (FileStream fs = File.Create(fileName))
  397. using (StreamWriter sw = new StreamWriter(fs, Encoding.Default))
  398. {
  399. foreach (var item in collection)
  400. sw.WriteLine(item);
  401. }
  402. }
  403. public static void ToFile<T>(this IEnumerable<T> collection, Func<T, string> toString, string fileName)
  404. {
  405. collection.Select(toString).ToFile(fileName);
  406. }
  407. public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
  408. {
  409. DataTable table = new DataTable();
  410. List<MemberEntry<T>> members = MemberEntryFactory.GenerateList<T>();
  411. table.Columns.AddRange(members.Select(m => new DataColumn(m.Name, m.MemberInfo.ReturningType())).ToArray());
  412. foreach (var e in collection)
  413. table.Rows.Add(members.Select(m => m.Getter(e)).ToArray());
  414. return table;
  415. }
  416. #region String Tables
  417. public static string[,] ToStringTable<T>(this IEnumerable<T> collection)
  418. {
  419. List<MemberEntry<T>> members = MemberEntryFactory.GenerateList<T>();
  420. string[,] result = new string[members.Count, collection.Count() + 1];
  421. for (int i = 0; i < members.Count; i++)
  422. result[i, 0] = members[i].Name;
  423. int j = 1;
  424. foreach (var item in collection)
  425. {
  426. for (int i = 0; i < members.Count; i++)
  427. result[i, j] = members[i].Getter(item).TryCC(a => a.ToString()) ?? "";
  428. j++;
  429. }
  430. return result;
  431. }
  432. public static string FormatTable(this string[,] table)
  433. {
  434. return FormatTable(table, true);
  435. }
  436. public static string FormatTable(this string[,] table, bool longHeaders)
  437. {
  438. int width = table.GetLength(0);
  439. int height = table.GetLength(1);
  440. int start = height == 1 ? 0 : (longHeaders ? 0 : 1);
  441. int[] lengths = 0.To(width).Select(i => Math.Max(3, start.To(height).Max(j => table[i, j].Length))).ToArray();
  442. return 0.To(height).Select(j => 0.To(width).ToString(i => table[i, j].PadChopRight(lengths[i]), " ")).ToString("\r\n");
  443. }
  444. public static void WriteFormattedStringTable<T>(this IEnumerable<T> collection, TextWriter textWriter, string title, bool longHeaders)
  445. {
  446. textWriter.WriteLine();
  447. if (title.HasText())
  448. textWriter.WriteLine(title);
  449. textWriter.WriteLine(collection.ToStringTable().FormatTable(longHeaders));
  450. textWriter.WriteLine();
  451. }
  452. public static void ToConsoleTable<T>(this IEnumerable<T> collection, string title = null, bool longHeader = false)
  453. {
  454. collection.WriteFormattedStringTable(Console.Out, title, longHeader);
  455. }
  456. public static string ToFormattedTable<T>(this IEnumerable<T> collection, string title = null, bool longHeader = false)
  457. {
  458. StringBuilder sb = new StringBuilder();
  459. using (StringWriter sw = new StringWriter(sb))
  460. collection.WriteFormattedStringTable(sw, title, longHeader);
  461. return sb.ToString();
  462. }
  463. public static string ToWikiTable<T>(this IEnumerable<T> collection)
  464. {
  465. string[,] table = collection.ToStringTable();
  466. string str = "{| class=\"data\"\r\n" + 0.To(table.GetLength(1))
  467. .Select(i => (i == 0 ? "! " : "| ") + table.Row(i).ToString(o => o == null ? "" : o.ToString(), i == 0 ? " !! " : " || "))
  468. .ToString("\r\n|-\r\n") + "\r\n|}";
  469. return str;
  470. }
  471. #endregion
  472. #region Min Max
  473. public static T WithMin<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
  474. where V : IComparable<V>
  475. {
  476. T result = default(T);
  477. bool hasMin = false;
  478. V min = default(V);
  479. foreach (var item in collection)
  480. {
  481. V val = valueSelector(item);
  482. if (!hasMin || val.CompareTo(min) < 0)
  483. {
  484. hasMin = true;
  485. min = val;
  486. result = item;
  487. }
  488. }
  489. return result;
  490. }
  491. public static T WithMax<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
  492. where V : IComparable<V>
  493. {
  494. T result = default(T);
  495. bool hasMax = false;
  496. V max = default(V);
  497. foreach (var item in collection)
  498. {
  499. V val = valueSelector(item);
  500. if (!hasMax || val.CompareTo(max) > 0)
  501. {
  502. hasMax = true;
  503. max = val;
  504. result = item;
  505. }
  506. }
  507. return result;
  508. }
  509. public static MinMax<T> WithMinMaxPair<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
  510. where V : IComparable<V>
  511. {
  512. T withMin = default(T), withMax = default(T);
  513. bool hasMin = false, hasMax = false;
  514. V min = default(V), max = default(V);
  515. foreach (var item in collection)
  516. {
  517. V val = valueSelector(item);
  518. if (!hasMax || val.CompareTo(max) > 0)
  519. {
  520. hasMax = true;
  521. max = val;
  522. withMax = item;
  523. }
  524. if (!hasMin || val.CompareTo(min) < 0)
  525. {
  526. hasMin = true;
  527. min = val;
  528. withMin = item;
  529. }
  530. }
  531. return new MinMax<T>(withMin, withMax);
  532. }
  533. public static Interval<T> MinMaxPair<T>(this IEnumerable<T> collection)
  534. where T : struct, IComparable<T>, IEquatable<T>
  535. {
  536. bool has = false;
  537. T min = default(T), max = default(T);
  538. foreach (var item in collection)
  539. {
  540. if (!has)
  541. {
  542. has = true;
  543. min = max = item;
  544. }
  545. else
  546. {
  547. if (item.CompareTo(max) > 0)
  548. max = item;
  549. if (item.CompareTo(min) < 0)
  550. min = item;
  551. }
  552. }
  553. return new Interval<T>(min, max);
  554. }
  555. public static Interval<V> MinMaxPair<T, V>(this IEnumerable<T> collection, Func<T, V> valueSelector)
  556. where V : struct, IComparable<V>, IEquatable<V>
  557. {
  558. bool has = false;
  559. V min = default(V), max = default(V);
  560. foreach (var item in collection)
  561. {
  562. V val = valueSelector(item);
  563. if (!has)
  564. {
  565. has = true;
  566. min = max = val;
  567. }
  568. else
  569. {
  570. if (val.CompareTo(max) > 0)
  571. max = val;
  572. if (val.CompareTo(min) < 0)
  573. min = val;
  574. }
  575. }
  576. return new Interval<V>(min, max);
  577. }
  578. #endregion
  579. #region Operation
  580. public static IEnumerable<T> Concat<T>(params IEnumerable<T>[] collections)
  581. {
  582. foreach (var collection in collections)
  583. {
  584. foreach (var item in collection)
  585. {
  586. yield return item;
  587. }
  588. }
  589. }
  590. public static IEnumerable<S> BiSelect<T, S>(this IEnumerable<T> collection, Func<T, T, S> func)
  591. {
  592. return BiSelect(collection, func, BiSelectOptions.None);
  593. }
  594. public static IEnumerable<S> BiSelect<T, S>(this IEnumerable<T> collection, Func<T, T, S> func, BiSelectOptions options)
  595. {
  596. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  597. {
  598. if (!enumerator.MoveNext())
  599. yield break;
  600. T firstItem = enumerator.Current;
  601. if (options == BiSelectOptions.Initial || options == BiSelectOptions.InitialAndFinal)
  602. yield return func(default(T), firstItem);
  603. T lastItem = firstItem;
  604. while (enumerator.MoveNext())
  605. {
  606. T item = enumerator.Current;
  607. yield return func(lastItem, item);
  608. lastItem = item;
  609. }
  610. if (options == BiSelectOptions.Final || options == BiSelectOptions.InitialAndFinal)
  611. yield return func(lastItem, default(T));
  612. if (options == BiSelectOptions.Circular)
  613. yield return func(lastItem, firstItem);
  614. }
  615. }
  616. //return one element more
  617. public static IEnumerable<S> SelectAggregate<T, S>(this IEnumerable<T> collection, S seed, Func<S, T, S> aggregate)
  618. {
  619. yield return seed;
  620. foreach (var item in collection)
  621. {
  622. seed = aggregate(seed, item);
  623. yield return seed;
  624. }
  625. }
  626. public static IEnumerable<IEnumerable<T>> CartesianProduct<T>(this IEnumerable<IEnumerable<T>> sequences)
  627. {
  628. IEnumerable<ImmutableStack<T>> emptyProduct = new[] { ImmutableStack<T>.Empty };
  629. var result = sequences.Aggregate(
  630. emptyProduct,
  631. (accumulator, sequence) =>
  632. from accseq in accumulator
  633. from item in sequence
  634. select accseq.Push(item));
  635. return result.Select(a => a.Reverse());
  636. }
  637. public static List<IGrouping<T, T>> GroupWhen<T>(this IEnumerable<T> collection, Func<T, bool> isGroupKey)
  638. {
  639. return GroupWhen(collection, isGroupKey, false);
  640. }
  641. public static List<IGrouping<T, T>> GroupWhen<T>(this IEnumerable<T> collection, Func<T, bool> isGroupKey, bool includeKeyInGroup)
  642. {
  643. List<IGrouping<T, T>> result = new List<IGrouping<T, T>>();
  644. Grouping<T, T> group = null;
  645. foreach (var item in collection)
  646. {
  647. if (isGroupKey(item))
  648. {
  649. group = new Grouping<T, T>(item);
  650. if (includeKeyInGroup)
  651. group.Add(item);
  652. result.Add(group);
  653. }
  654. else
  655. {
  656. if (group != null)
  657. group.Add(item);
  658. }
  659. }
  660. return result;
  661. }
  662. public static IEnumerable<IGrouping<K, T>> GroupWhenChange<T, K>(this IEnumerable<T> collection, Func<T, K> getGroupKey)
  663. {
  664. Grouping<K, T> current = null;
  665. foreach (var item in collection)
  666. {
  667. if (current == null)
  668. {
  669. current = new Grouping<K, T>(getGroupKey(item));
  670. current.Add(item);
  671. }
  672. else if (current.Key.Equals(getGroupKey(item)))
  673. {
  674. current.Add(item);
  675. }
  676. else
  677. {
  678. yield return current;
  679. current = new Grouping<K, T>(getGroupKey(item));
  680. current.Add(item);
  681. }
  682. }
  683. if (current != null)
  684. yield return current;
  685. }
  686. public static IEnumerable<T> Distinct<T, S>(this IEnumerable<T> collection, Func<T, S> func)
  687. {
  688. return collection.Distinct(new LambdaComparer<T, S>(func));
  689. }
  690. public static IEnumerable<T> Distinct<T, S>(this IEnumerable<T> collection, Func<T, S> func, IEqualityComparer<S> comparer)
  691. {
  692. return collection.Distinct(new LambdaComparer<T, S>(func, comparer, null));
  693. }
  694. public static IEnumerable<List<T>> GroupsOf<T>(this IEnumerable<T> collection, int groupSize)
  695. {
  696. List<T> newList = new List<T>(groupSize);
  697. foreach (var item in collection)
  698. {
  699. newList.Add(item);
  700. if (newList.Count == groupSize)
  701. {
  702. yield return newList;
  703. newList = new List<T>();
  704. }
  705. }
  706. if (newList.Count != 0)
  707. yield return newList;
  708. }
  709. public static IEnumerable<T> Slice<T>(this IEnumerable<T> collection, int firstIncluded, int toNotIncluded)
  710. {
  711. return collection.Skip(firstIncluded).Take(toNotIncluded - firstIncluded);
  712. }
  713. public static IOrderedEnumerable<T> Order<T>(this IEnumerable<T> collection) where T : IComparable<T>
  714. {
  715. return collection.OrderBy(a => a);
  716. }
  717. public static IOrderedEnumerable<T> OrderDescending<T>(this IEnumerable<T> collection) where T : IComparable<T>
  718. {
  719. return collection.OrderByDescending(a => a);
  720. }
  721. #endregion
  722. #region Zip
  723. public static IEnumerable<Tuple<A, B>> Zip<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB)
  724. {
  725. using (var enumA = colA.GetEnumerator())
  726. using (var enumB = colB.GetEnumerator())
  727. {
  728. while (enumA.MoveNext() && enumB.MoveNext())
  729. {
  730. yield return new Tuple<A, B>(enumA.Current, enumB.Current);
  731. }
  732. }
  733. }
  734. public static void ZipForeach<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB, Action<A, B> actions)
  735. {
  736. using (var enumA = colA.GetEnumerator())
  737. using (var enumB = colB.GetEnumerator())
  738. {
  739. while (enumA.MoveNext() && enumB.MoveNext())
  740. {
  741. actions(enumA.Current, enumB.Current);
  742. }
  743. }
  744. }
  745. public static IEnumerable<Tuple<A, B>> ZipStrict<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB)
  746. {
  747. using (var enumA = colA.GetEnumerator())
  748. using (var enumB = colB.GetEnumerator())
  749. {
  750. while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
  751. {
  752. yield return new Tuple<A, B>(enumA.Current, enumB.Current);
  753. }
  754. }
  755. }
  756. public static IEnumerable<R> ZipStrict<A, B, R>(this IEnumerable<A> colA, IEnumerable<B> colB, Func<A, B, R> mixer)
  757. {
  758. using (var enumA = colA.GetEnumerator())
  759. using (var enumB = colB.GetEnumerator())
  760. {
  761. while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
  762. {
  763. yield return mixer(enumA.Current, enumB.Current);
  764. }
  765. }
  766. }
  767. public static void ZipForeachStrict<A, B>(this IEnumerable<A> colA, IEnumerable<B> colB, Action<A, B> action)
  768. {
  769. using (var enumA = colA.GetEnumerator())
  770. using (var enumB = colB.GetEnumerator())
  771. {
  772. while (AssertoTwo(enumA.MoveNext(), enumB.MoveNext()))
  773. {
  774. action(enumA.Current, enumB.Current);
  775. }
  776. }
  777. }
  778. static bool AssertoTwo(bool nextA, bool nextB)
  779. {
  780. if (nextA != nextB)
  781. if (nextA)
  782. throw new InvalidOperationException("Second collection is shorter");
  783. else
  784. throw new InvalidOperationException("First collection is shorter");
  785. else
  786. return nextA;
  787. }
  788. #endregion
  789. #region Conversions
  790. public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source)
  791. {
  792. return new HashSet<T>(source);
  793. }
  794. public static HashSet<T> ToHashSet<T>(this IEnumerable<T> source, IEqualityComparer<T> comparer)
  795. {
  796. return new HashSet<T>(source, comparer);
  797. }
  798. public static ReadOnlyCollection<T> ToReadOnly<T>(this IEnumerable<T> collection)
  799. {
  800. return collection == null ? null :
  801. collection as ReadOnlyCollection<T> ??
  802. (collection as List<T> ?? collection.ToList()).AsReadOnly();
  803. }
  804. public static IEnumerable<T> AsThreadSafe<T>(this IEnumerable<T> source)
  805. {
  806. return new TreadSafeEnumerator<T>(source);
  807. }
  808. public static IEnumerable<T> ToProgressEnumerator<T>(this IEnumerable<T> source, out IProgressInfo pi)
  809. {
  810. pi = new ProgressEnumerator<T>(source, source.Count());
  811. return (IEnumerable<T>)pi;
  812. }
  813. public static void PushRange<T>(this Stack<T> stack, IEnumerable<T> elements)
  814. {
  815. foreach (var item in elements)
  816. stack.Push(item);
  817. }
  818. public static void EnqueueRange<T>(this Queue<T> queue, IEnumerable<T> elements)
  819. {
  820. foreach (var item in elements)
  821. queue.Enqueue(item);
  822. }
  823. public static void AddRange<T>(this HashSet<T> hashset, IEnumerable<T> coleccion)
  824. {
  825. foreach (var item in coleccion)
  826. {
  827. hashset.Add(item);
  828. }
  829. }
  830. public static bool TryContains<T>(this HashSet<T> hashset, T element)
  831. {
  832. if (hashset == null)
  833. return false;
  834. return hashset.Contains(element);
  835. }
  836. #endregion
  837. public static IEnumerable<R> JoinStrict<K, C, S, R>(
  838. IEnumerable<C> currentCollection,
  839. IEnumerable<S> shouldCollection,
  840. Func<C, K> currentKeySelector,
  841. Func<S, K> shouldKeySelector,
  842. Func<C, S, R> resultSelector, string action)
  843. {
  844. var currentDictionary = currentCollection.ToDictionary(currentKeySelector);
  845. var shouldDictionary = shouldCollection.ToDictionary(shouldKeySelector);
  846. var extra = currentDictionary.Keys.Where(k => !shouldDictionary.ContainsKey(k)).ToList();
  847. var lacking = shouldDictionary.Keys.Where(k => !currentDictionary.ContainsKey(k)).ToList();
  848. if (extra.Count != 0)
  849. if (lacking.Count != 0)
  850. throw new InvalidOperationException("Error {0}\r\n Extra: {1}\r\n Lacking: {2}".Formato(action, extra.ToString(", "), lacking.ToString(", ")));
  851. else
  852. throw new InvalidOperationException("Error {0}\r\n Extra: {1}".Formato(action, extra.ToString(", ")));
  853. else
  854. if (lacking.Count != 0)
  855. throw new InvalidOperationException("Error {0}\r\n Lacking: {1}".Formato(action, lacking.ToString(", ")));
  856. return currentDictionary.Select(p => resultSelector(p.Value, shouldDictionary[p.Key]));
  857. }
  858. public static JoinStrictResult<C, S, R> JoinStrict<K, C, S, R>(
  859. IEnumerable<C> currentCollection,
  860. IEnumerable<S> shouldCollection,
  861. Func<C, K> currentKeySelector,
  862. Func<S, K> shouldKeySelector,
  863. Func<C, S, R> resultSelector)
  864. {
  865. var currentDictionary = currentCollection.ToDictionary(currentKeySelector);
  866. var newDictionary = shouldCollection.ToDictionary(shouldKeySelector);
  867. HashSet<K> commonKeys = currentDictionary.Keys.ToHashSet();
  868. commonKeys.IntersectWith(newDictionary.Keys);
  869. return new JoinStrictResult<C, S, R>
  870. {
  871. Extra = currentDictionary.Where(e => !newDictionary.ContainsKey(e.Key)).Select(e => e.Value).ToList(),
  872. Lacking = newDictionary.Where(e => !currentDictionary.ContainsKey(e.Key)).Select(e => e.Value).ToList(),
  873. Result = commonKeys.Select(k => resultSelector(currentDictionary[k], newDictionary[k])).ToList()
  874. };
  875. }
  876. public static IEnumerable<Iteration<T>> Iterate<T>(this IEnumerable<T> collection)
  877. {
  878. using (IEnumerator<T> enumerator = collection.GetEnumerator())
  879. {
  880. if (!enumerator.MoveNext())
  881. {
  882. yield break;
  883. }
  884. bool isFirst = true;
  885. bool isLast = false;
  886. int index = 0;
  887. while (!isLast)
  888. {
  889. T current = enumerator.Current;
  890. isLast = !enumerator.MoveNext();
  891. yield return new Iteration<T>(current, isFirst, isLast, index++);
  892. isFirst = false;
  893. }
  894. }
  895. }
  896. }
  897. public class JoinStrictResult<O, N, R>
  898. {
  899. public List<O> Extra;
  900. public List<N> Lacking;
  901. public List<R> Result;
  902. }
  903. public enum BiSelectOptions
  904. {
  905. None,
  906. Initial,
  907. Final,
  908. InitialAndFinal,
  909. Circular,
  910. }
  911. public class Iteration<T>
  912. {
  913. readonly T value;
  914. readonly bool isFirst;
  915. readonly bool isLast;
  916. readonly int position;
  917. internal Iteration(T value, bool isFirst, bool isLast, int position)
  918. {
  919. this.value = value;
  920. this.isFirst = isFirst;
  921. this.isLast = isLast;
  922. this.position = position;
  923. }
  924. public T Value { get { return value; } }
  925. public bool IsFirst { get { return isFirst; } }
  926. public bool IsLast { get { return isLast; } }
  927. public int Position { get { return position; } }
  928. public bool IsEven { get { return position % 2 == 0; } }
  929. public bool IsOdd { get { return position % 1 == 0; } }
  930. }
  931. }