Skip to content

Commit 9d8b647

Browse files
author
Oren (electricessence)
committed
Added read only collection extensions.
1 parent 83cb5a0 commit 9d8b647

File tree

2 files changed

+157
-26
lines changed

2 files changed

+157
-26
lines changed

Open.RandomizationExtensions.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Part of the "Open" set of libraries.</Description>
1414
<RepositoryUrl>https://github.com/electricessence/Open.RandomizationExtensions</RepositoryUrl>
1515
<RepositoryType>Git</RepositoryType>
1616
<PackageTags>random select</PackageTags>
17-
<Version>1.0.1</Version>
17+
<Version>1.1.0</Version>
1818
</PropertyGroup>
1919

2020
<ItemGroup>

Random.cs

Lines changed: 156 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)