Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

An extension method on IEnumerable needed for shuffling [duplicate]

Tags:

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); } 
like image 441
Gulshan Avatar asked Apr 27 '11 16:04

Gulshan


2 Answers

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];         }     } } 
like image 143
LukeH Avatar answered Sep 29 '22 20:09

LukeH


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(); } 
like image 27
Anton Gogolev Avatar answered Sep 29 '22 21:09

Anton Gogolev