@@ -177,17 +177,9 @@ public static int RandomSelectIndex<T>(this in ReadOnlySpan<T> source, IEnumerab
177177 public static int RandomSelectIndex < T > ( this in Span < T > source , IEnumerable < T > exclusion = null )
178178 => RandomSelectIndex ( ( ReadOnlySpan < T > ) source , exclusion ) ;
179179
180- /// <summary>
181- /// Randomly selects an index from the source.
182- /// Will not return indexes that are contained in the optional exclusion set.
183- /// </summary>
184- /// <typeparam name="T">The generic type of the source.</typeparam>
185- /// <param name="source">The source collection.</param>
186- /// <param name="exclusion">The optional values to exclude from selection.</param>
187- /// <returns>The index selected.</returns>
188- public static int RandomSelectIndex < T > ( this ICollection < T > source , IEnumerable < T > exclusion = null )
180+ static int RandomSelectIndex < T > ( int count , IEnumerable < T > source , IEnumerable < T > exclusion )
189181 {
190- if ( source . Count == 0 )
182+ if ( count == 0 )
191183 return - 1 ;
192184
193185 HashSet < T > setCreated = null ;
@@ -197,9 +189,9 @@ public static int RandomSelectIndex<T>(this ICollection<T> source, IEnumerable<T
197189 : exclusion as ISet < T > ?? ( setCreated = new HashSet < T > ( exclusion ) ) ;
198190
199191 if ( exclusionSet == null || exclusionSet . Count == 0 )
200- return R . Value . Next ( source . Count ) ;
192+ return R . Value . Next ( count ) ;
201193
202- using ( var indexesTemp = ArrayPool < int > . Shared . RentDisposable ( source . Count ) )
194+ using ( var indexesTemp = ArrayPool < int > . Shared . RentDisposable ( count ) )
203195 {
204196 var indexes = indexesTemp . Array ;
205197 var i = - 1 ;
@@ -219,6 +211,28 @@ public static int RandomSelectIndex<T>(this ICollection<T> source, IEnumerable<T
219211 }
220212 }
221213
214+ /// <summary>
215+ /// Randomly selects an index from the source.
216+ /// Will not return indexes that are contained in the optional exclusion set.
217+ /// </summary>
218+ /// <typeparam name="T">The generic type of the source.</typeparam>
219+ /// <param name="source">The source collection.</param>
220+ /// <param name="exclusion">The optional values to exclude from selection.</param>
221+ /// <returns>The index selected.</returns>
222+ public static int RandomSelectIndex < T > ( this IReadOnlyCollection < T > source , IEnumerable < T > exclusion = null )
223+ => RandomSelectIndex ( source . Count , source , exclusion ) ;
224+
225+ /// <summary>
226+ /// Randomly selects an index from the source.
227+ /// Will not return indexes that are contained in the optional exclusion set.
228+ /// </summary>
229+ /// <typeparam name="T">The generic type of the source.</typeparam>
230+ /// <param name="source">The source collection.</param>
231+ /// <param name="exclusion">The optional values to exclude from selection.</param>
232+ /// <returns>The index selected.</returns>
233+ public static int RandomSelectIndex < T > ( this ICollection < T > source , IEnumerable < T > exclusion = null )
234+ => RandomSelectIndex ( source . Count , source , exclusion ) ;
235+
222236 /// <summary>
223237 /// Randomly selects an index from the source.
224238 /// Will not return indexes that are contained in the optional exclusion set.
@@ -272,34 +286,25 @@ public static int RandomSelectIndexExcept<T>(this in ReadOnlySpan<T> source, T e
272286 public static int RandomSelectIndexExcept < T > ( this in Span < T > source , T exclusion , params T [ ] others )
273287 => RandomSelectIndexExcept ( ( ReadOnlySpan < T > ) source , exclusion , others ) ;
274288
275- /// <summary>
276- /// Randomly selects an index from the source.
277- /// Will not return indexes that are contained in the optional exclusion set.
278- /// </summary>
279- /// <typeparam name="T">The generic type of the source.</typeparam>
280- /// <param name="source">The source collection.</param>
281- /// <param name="exclusion">A value to exclude from selection.</param>
282- /// <param name="others">The additional set of optional values to exclude from selection.</param>
283- /// <returns>The index selected.</returns>
284- public static int RandomSelectIndexExcept < T > ( this ICollection < T > source , T exclusion , params T [ ] others )
289+ static int RandomSelectIndexExcept < T > ( int count , IEnumerable < T > source , T exclusion , T [ ] others )
285290 {
286- if ( source . Count == 0 )
291+ if ( count == 0 )
287292 return - 1 ;
288293
289294 if ( others . Length != 0 )
290295 {
291296 var setCreated = new HashSet < T > ( others ) { exclusion } ;
292297 try
293298 {
294- return RandomSelectIndex ( source , setCreated ) ;
299+ return RandomSelectIndex ( count , source , setCreated ) ;
295300 }
296301 finally
297302 {
298303 setCreated . Clear ( ) ;
299304 }
300305 }
301306
302- using ( var indexesTemp = ArrayPool < int > . Shared . RentDisposable ( source . Count ) )
307+ using ( var indexesTemp = ArrayPool < int > . Shared . RentDisposable ( count ) )
303308 {
304309 var indexes = indexesTemp . Array ;
305310 var i = - 1 ;
@@ -314,6 +319,30 @@ public static int RandomSelectIndexExcept<T>(this ICollection<T> source, T exclu
314319 }
315320 }
316321
322+ /// <summary>
323+ /// Randomly selects an index from the source.
324+ /// Will not return indexes that are contained in the optional exclusion set.
325+ /// </summary>
326+ /// <typeparam name="T">The generic type of the source.</typeparam>
327+ /// <param name="source">The source collection.</param>
328+ /// <param name="exclusion">A value to exclude from selection.</param>
329+ /// <param name="others">The additional set of optional values to exclude from selection.</param>
330+ /// <returns>The index selected.</returns>
331+ public static int RandomSelectIndexExcept < T > ( this IReadOnlyCollection < T > source , T exclusion , params T [ ] others )
332+ => RandomSelectIndexExcept ( source . Count , source , exclusion , others ) ;
333+
334+ /// <summary>
335+ /// Randomly selects an index from the source.
336+ /// Will not return indexes that are contained in the optional exclusion set.
337+ /// </summary>
338+ /// <typeparam name="T">The generic type of the source.</typeparam>
339+ /// <param name="source">The source collection.</param>
340+ /// <param name="exclusion">A value to exclude from selection.</param>
341+ /// <param name="others">The additional set of optional values to exclude from selection.</param>
342+ /// <returns>The index selected.</returns>
343+ public static int RandomSelectIndexExcept < T > ( this ICollection < T > source , T exclusion , params T [ ] others )
344+ => RandomSelectIndexExcept ( source . Count , source , exclusion , others ) ;
345+
317346 /// <summary>
318347 /// Attempts to select an index at random from the source and returns the value from it..
319348 /// Will not select indexes that are contained in the optional exclusion set.
@@ -354,6 +383,30 @@ public static bool TryRandomSelectOne<T>(
354383 IEnumerable < T > exclusion = null )
355384 => TryRandomSelectOne ( ( ReadOnlySpan < T > ) source , out value , exclusion ) ;
356385
386+ /// <summary>
387+ /// Selects an index at random from the source and returns the value from it.
388+ /// Will not select indexes that are contained in the optional exclusion set.
389+ /// </summary>
390+ /// <typeparam name="T">The generic type of the source.</typeparam>
391+ /// <param name="source">The source span.</param>
392+ /// <param name="exclusion">The optional values to exclude from selection.</param>
393+ /// <returns>The value selected.</returns>
394+ public static T RandomSelectOne < T > (
395+ this IReadOnlyCollection < T > source ,
396+ IEnumerable < T > exclusion = null )
397+ {
398+ if ( source . Count == 0 )
399+ throw new InvalidOperationException ( "Source collection is empty." ) ;
400+
401+ var index = RandomSelectIndex ( source , exclusion ) ;
402+ if ( index == - 1 )
403+ throw new InvalidOperationException ( "Exclusion set invalidates the source. No possible value can be selected." ) ;
404+
405+ return source is IReadOnlyList < T > list
406+ ? list [ index ]
407+ : source . ElementAt ( index ) ;
408+ }
409+
357410 /// <summary>
358411 /// Selects an index at random from the source and returns the value from it.
359412 /// Will not select indexes that are contained in the optional exclusion set.
@@ -378,6 +431,34 @@ public static T RandomSelectOne<T>(
378431 : source . ElementAt ( index ) ;
379432 }
380433
434+ /// <summary>
435+ /// Attempts to select an index at random from the source and returns the value from it..
436+ /// Will not select indexes that are contained in the optional exclusion set.
437+ /// </summary>
438+ /// <typeparam name="T">The generic type of the source.</typeparam>
439+ /// <param name="source">The source collection.</param>
440+ /// <param name="value">The value selected.</param>
441+ /// <param name="exclusion">The optional values to exclude from selection.</param>
442+ /// <returns>True if a valid value was selected.</returns>
443+ public static bool TryRandomSelectOne < T > (
444+ this IReadOnlyCollection < T > source ,
445+ out T value ,
446+ IEnumerable < T > exclusion = null )
447+ {
448+ var index = RandomSelectIndex ( source , exclusion ) ;
449+ if ( index == - 1 )
450+ {
451+ value = default ;
452+ return false ;
453+ }
454+
455+ value = source is IReadOnlyList < T > list
456+ ? list [ index ]
457+ : source . ElementAt ( index ) ;
458+
459+ return true ;
460+ }
461+
381462 /// <summary>
382463 /// Attempts to select an index at random from the source and returns the value from it..
383464 /// Will not select indexes that are contained in the optional exclusion set.
@@ -458,7 +539,35 @@ public static bool TryRandomSelectOneExcept<T>(
458539 /// <param name="excluding">The value to exclude from selection.</param>
459540 /// <param name="others">The additional set of optional values to exclude from selection.</param>
460541 /// <returns>True if a valid value was selected.</returns>
542+ public static bool TryRandomSelectOneExcept < T > (
543+ this IReadOnlyCollection < T > source ,
544+ out T value ,
545+ T excluding , params T [ ] others )
546+ {
547+ var index = RandomSelectIndexExcept ( source , excluding , others ) ;
548+ if ( index == - 1 )
549+ {
550+ value = default ;
551+ return false ;
552+ }
553+
554+ value = source is IReadOnlyList < T > list
555+ ? list [ index ]
556+ : source . ElementAt ( index ) ;
461557
558+ return true ;
559+ }
560+
561+ /// <summary>
562+ /// Attempts to select an index at random from the source and returns the value from it..
563+ /// Will not select indexes that are contained in the optional exclusion set.
564+ /// </summary>
565+ /// <typeparam name="T">The generic type of the source.</typeparam>
566+ /// <param name="source">The source collection.</param>
567+ /// <param name="value">The value selected.</param>
568+ /// <param name="excluding">The value to exclude from selection.</param>
569+ /// <param name="others">The additional set of optional values to exclude from selection.</param>
570+ /// <returns>True if a valid value was selected.</returns>
462571 public static bool TryRandomSelectOneExcept < T > (
463572 this ICollection < T > source ,
464573 out T value ,
@@ -514,6 +623,28 @@ public static T RandomSelectOneExcept<T>(
514623 T excluding , params T [ ] others )
515624 => RandomSelectOneExcept ( ( ReadOnlySpan < T > ) source , excluding , others ) ;
516625
626+ /// <summary>
627+ /// Selects an index at random from the source and returns the value from it.
628+ /// Will not select indexes that are contained in the optional exclusion set.
629+ /// </summary>
630+ /// <typeparam name="T">The generic type of the source.</typeparam>
631+ /// <param name="source">The source collection.</param>
632+ /// <param name="excluding">The value to exclude from selection.</param>
633+ /// <param name="others">The additional set of optional values to exclude from selection.</param>
634+ /// <returns>The value selected.</returns>
635+ public static T RandomSelectOneExcept < T > (
636+ this IReadOnlyCollection < T > source ,
637+ T excluding , params T [ ] others )
638+ {
639+ if ( source . Count == 0 )
640+ throw new InvalidOperationException ( "Source collection is empty." ) ;
641+
642+ if ( source . TryRandomSelectOneExcept ( out T value , excluding , others ) )
643+ return value ;
644+
645+ throw new InvalidOperationException ( "Exclusion set invalidates the source. No possible value can be selected." ) ;
646+ }
647+
517648 /// <summary>
518649 /// Selects an index at random from the source and returns the value from it.
519650 /// Will not select indexes that are contained in the optional exclusion set.
0 commit comments