I need an extension method which will shuffle an IEnumerable<T>
. It can also take an int
to specify the size of the returned IEnumerable
. Better keeping Immutability of the IEnumerable
. My current solution for IList
-
public static IList<T> Shuffle<T>(this IList<T> list, int size) { Random rnd = new Random(); var res = new T[size]; res[0] = list[0]; for (int i = 1; i < size; i++) { int j = rnd.Next(i); res[i] = res[j]; res[j] = list[i]; } return res; } public static IList<T> Shuffle<T>(this IList<T> list) { return list.Shuffle(list.Count); }
You can use a Fisher-Yates-Durstenfeld shuffle. There's no need to explicitly pass a size argument to the method itself, you can simply tack on a call to Take
if you don't need the entire sequence:
var shuffled = originalSequence.Shuffle().Take(5); // ... public static class EnumerableExtensions { public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source) { return source.Shuffle(new Random()); } public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source, Random rng) { if (source == null) throw new ArgumentNullException(nameof(source)); if (rng == null) throw new ArgumentNullException(nameof(rng)); return source.ShuffleIterator(rng); } private static IEnumerable<T> ShuffleIterator<T>( this IEnumerable<T> source, Random rng) { var buffer = source.ToList(); for (int i = 0; i < buffer.Count; i++) { int j = rng.Next(i, buffer.Count); yield return buffer[j]; buffer[j] = buffer[i]; } } }
With some LINQ love:
public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> list, int size) { var r = new Random(); var shuffledList = list. Select(x => new { Number = r.Next(), Item = x }). OrderBy(x => x.Number). Select(x => x.Item). Take(size); // Assume first @size items is fine return shuffledList.ToList(); }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With