11using System ;
2- using System . Collections ;
32using System . Collections . Generic ;
43using System . Linq ;
54
@@ -15,25 +14,48 @@ public static class LinqRandomizer
1514
1615 #region Public LINQ methods
1716
18- ///<summary>Returns a random element from a collection</summary>
19- ///<typeparam name="T">The element type of the collection</typeparam>
20- ///<param cref="elements">An ICollection of the specified type <seealso cref="T" /></param>
21- ///<exception cref="System.ArgumentOutOfRangeException">Thrown when the list is empty</exception>
22- public static T Random < T > ( this ICollection < T > elements )
17+ ///<summary>Returns a random element from a collection.</summary>
18+ ///<typeparam name="T">The element type of the collection.</typeparam>
19+ ///<param cref="elements">An ICollection of the specified type <seealso cref="T" />.</param>
20+ ///<exception cref="System.ArgumentOutOfRangeException">Thrown if the ICollection is empty.</exception>
21+ ///<exception cref="AMT.LinqExtensions.AllElementsExcluded">Thrown if all elements have been excluded.</exception>
22+ public static T Random < T > ( this ICollection < T > elements , IEnumerable < T > exclusions = null )
2323 {
2424 // Delegate to overload taking IEnumerable
25- return Random < T > ( elements as IEnumerable < T > ) ;
25+ return Random < T > ( elements as IEnumerable < T > , exclusions ) ;
2626 }
2727
28- ///<summary>Returns a random element using an enumerable</summary>
29- ///<typeparam name="T">The element type of the enumerable</typeparam>
30- ///<param cref="elements">An IEnumerable of the specified type <seealso cref="T" /></param>
31- ///<exception cref="System.ArgumentOutOfRangeException">Thrown when the list is empty</exception>
32- public static T Random < T > ( this IEnumerable < T > elements )
28+
29+ ///<summary>Returns a random element using an enumerable.</summary>
30+ ///<typeparam name="T">The element type of the enumerable.</typeparam>
31+ ///<param cref="elements">An IEnumerable of the specified type <seealso cref="T" />.</param>
32+ ///<exception cref="System.ArgumentOutOfRangeException">Thrown if the IEnumerable is empty.</exception>
33+ ///<exception cref="AMT.LinqExtensions.AllElementsExcluded">Thrown if all elements have been excluded.</exception>
34+ public static T Random < T > ( this IEnumerable < T > elements , IEnumerable < T > exclusions = null )
3335 {
34- return elements . ElementAt ( _randomizer . Next ( 0 , elements . Count ( ) - 1 ) ) ;
36+ exclusions ??= new List < T > ( ) ;
37+
38+ T result ;
39+ int iterations = 0 ;
40+ do
41+ {
42+ result = elements . ElementAt ( _randomizer . Next ( 0 , elements . Count ( ) - 1 ) ) ;
43+ ++ iterations ;
44+ } while ( exclusions . Contains < T > ( result ) & & iterations < elements . Count ( ) ) ;
45+
46+ // TODO: this approach assumes that each element has been considered. If the randomizer
47+ // repeats some values, not all elements will be considered.
48+
49+ // Reached the end of the collection (possibly) without finding an element that is not excluded.
50+ if ( iterations == elements . Count ( ) && exclusions . Contains ( result ) )
51+ {
52+ throw new AllElementsExcluded ( ) ;
53+ }
54+
55+ return result ;
3556 }
3657
58+
3759 #endregion Public LINQ methods
3860 }
3961
0 commit comments