Distinct.cs 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Threading.Tasks;
  5. namespace System.Linq
  6. {
  7. public static partial class EnumerableEx
  8. {
  9. /// <summary>
  10. /// Returns elements with a distinct key value by using the default equality comparer to compare key values.
  11. /// </summary>
  12. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  13. /// <typeparam name="TKey">Key type.</typeparam>
  14. /// <param name="source">Source sequence.</param>
  15. /// <param name="keySelector">Key selector.</param>
  16. /// <returns>Sequence that contains the elements from the source sequence with distinct key values.</returns>
  17. public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
  18. {
  19. if (source == null)
  20. throw new ArgumentNullException(nameof(source));
  21. if (keySelector == null)
  22. throw new ArgumentNullException(nameof(keySelector));
  23. return source.Distinct_(keySelector, EqualityComparer<TKey>.Default);
  24. }
  25. /// <summary>
  26. /// Returns elements with a distinct key value by using the specified equality comparer to compare key values.
  27. /// </summary>
  28. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  29. /// <typeparam name="TKey">Key type.</typeparam>
  30. /// <param name="source">Source sequence.</param>
  31. /// <param name="keySelector">Key selector.</param>
  32. /// <param name="comparer">Comparer used to compare key values.</param>
  33. /// <returns>Sequence that contains the elements from the source sequence with distinct key values.</returns>
  34. public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  35. {
  36. if (source == null)
  37. throw new ArgumentNullException(nameof(source));
  38. if (keySelector == null)
  39. throw new ArgumentNullException(nameof(keySelector));
  40. if (comparer == null)
  41. throw new ArgumentNullException(nameof(comparer));
  42. return source.Distinct_(keySelector, comparer);
  43. }
  44. private static IEnumerable<TSource> Distinct_<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  45. {
  46. var set = new HashSet<TKey>(comparer);
  47. foreach (var item in source)
  48. {
  49. var key = keySelector(item);
  50. if (set.Add(key))
  51. yield return item;
  52. }
  53. }
  54. /// <summary>
  55. /// Returns consecutive distinct elements by using the default equality comparer to compare values.
  56. /// </summary>
  57. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  58. /// <param name="source">Source sequence.</param>
  59. /// <returns>Sequence without adjacent non-distinct elements.</returns>
  60. public static IEnumerable<TSource> DistinctUntilChanged<TSource>(this IEnumerable<TSource> source)
  61. {
  62. if (source == null)
  63. throw new ArgumentNullException(nameof(source));
  64. return source.DistinctUntilChanged_(x => x, EqualityComparer<TSource>.Default);
  65. }
  66. /// <summary>
  67. /// Returns consecutive distinct elements by using the specified equality comparer to compare values.
  68. /// </summary>
  69. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  70. /// <param name="source">Source sequence.</param>
  71. /// <param name="comparer">Comparer used to compare values.</param>
  72. /// <returns>Sequence without adjacent non-distinct elements.</returns>
  73. public static IEnumerable<TSource> DistinctUntilChanged<TSource>(this IEnumerable<TSource> source, IEqualityComparer<TSource> comparer)
  74. {
  75. if (source == null)
  76. throw new ArgumentNullException(nameof(source));
  77. if (comparer == null)
  78. throw new ArgumentNullException(nameof(comparer));
  79. return source.DistinctUntilChanged_(x => x, comparer);
  80. }
  81. /// <summary>
  82. /// Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values.
  83. /// </summary>
  84. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  85. /// <typeparam name="TKey">Key type.</typeparam>
  86. /// <param name="source">Source sequence.</param>
  87. /// <param name="keySelector">Key selector.</param>
  88. /// <returns>Sequence without adjacent non-distinct elements.</returns>
  89. public static IEnumerable<TSource> DistinctUntilChanged<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector)
  90. {
  91. if (source == null)
  92. throw new ArgumentNullException(nameof(source));
  93. if (keySelector == null)
  94. throw new ArgumentNullException(nameof(keySelector));
  95. return source.DistinctUntilChanged_(keySelector, EqualityComparer<TKey>.Default);
  96. }
  97. /// <summary>
  98. /// Returns consecutive distinct elements based on a key value by using the specified equality comparer to compare key values.
  99. /// </summary>
  100. /// <typeparam name="TSource">Source sequence element type.</typeparam>
  101. /// <typeparam name="TKey">Key type.</typeparam>
  102. /// <param name="source">Source sequence.</param>
  103. /// <param name="keySelector">Key selector.</param>
  104. /// <param name="comparer">Comparer used to compare key values.</param>
  105. /// <returns>Sequence without adjacent non-distinct elements.</returns>
  106. public static IEnumerable<TSource> DistinctUntilChanged<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  107. {
  108. if (source == null)
  109. throw new ArgumentNullException(nameof(source));
  110. if (keySelector == null)
  111. throw new ArgumentNullException(nameof(keySelector));
  112. if (comparer == null)
  113. throw new ArgumentNullException(nameof(comparer));
  114. return source.DistinctUntilChanged_(keySelector, comparer);
  115. }
  116. private static IEnumerable<TSource> DistinctUntilChanged_<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
  117. {
  118. var currentKey = default(TKey);
  119. var hasCurrentKey = false;
  120. foreach (var item in source)
  121. {
  122. var key = keySelector(item);
  123. var comparerEquals = false;
  124. if (hasCurrentKey)
  125. {
  126. comparerEquals = comparer.Equals(currentKey, key);
  127. }
  128. if (!hasCurrentKey || !comparerEquals)
  129. {
  130. hasCurrentKey = true;
  131. currentKey = key;
  132. yield return item;
  133. }
  134. }
  135. }
  136. }
  137. }