title: LINQ Select Customized Random-ness
body: <p>I have this code to select a random entry from a LINQ query</p>
  
  <pre><code>Random rand = new Random();
  int TotalFound = Query.Count();
  if (TotalFound &gt; 0) {
      int toSkip = rand.Next(0, TotalFound);
      Video Result = Query.Skip(toSkip).Take(1).First();
      return Result;
  } else
      return null;
  </code></pre>
  
  <p>Now, I want to add a column within Query called "Preference", which is a number between 0 and 10. A row with a Preference value of 8 would be twice as likely to be selected as a row with Preference value of 4. A value of 9.8 would be even more likely to be selected.</p>
  
  <p>What would be an effective way of implementing this algorithm?</p>
  
  <p>As a second step, I could add a parameter that allows 8 to be worth 3x or 4x a row set to 4, to fine-tune the results with an exponential curve instead of a linear curve.</p>
  
  <p>I'm really not sure about how to go to implement this efficiently.</p>
  
  <p><strong>Here's the solution I implemented</strong></p>
  
  <pre><code>public static int? SelectRandomId(IQueryable&lt;Media&gt; query, out int totalFound) {
      int? Result = null;
  
      // Pull list of ID and Preference from database.
      var IdList = query.Select(v =&gt; new { ID = v.MediaId, Preference = v.Preference }).ToList();
      totalFound = IdList.Count();
  
      // Calculate preferences average and total.
      int PreferenceCount = IdList.Where(v =&gt; v.Preference.HasValue).Count();
      int NoPreferenceCount = IdList.Count() - PreferenceCount;
      int PreferenceSum = IdList.Where(v =&gt; v.Preference.HasValue).Sum(v =&gt; PreferenceToInt(v.Preference.Value));
      // Use value 10 for every item if it is not specified for any.
      int PreferenceAvg = (PreferenceCount &gt; 0 ? PreferenceSum / PreferenceCount : 10);
      // Videos with no preference get the average value.
      int PreferenceTotal = PreferenceSum + NoPreferenceCount * PreferenceAvg;
  
      // Get a random number between zero and the sum of all the preferences
      Random rand = new Random();
      int number = rand.Next(0, PreferenceTotal);
  
      int rollingSumOfPreferences = 0;
  
      // Set default value in case a value doesn't get assigned by the loop.
      if (totalFound &gt; 0)
          Result = IdList[0].ID;
  
      // Select an index from the video list, but weighted by preference
      foreach (var item in IdList) {
          // Add the current item's preference to the rolling sum
          if (item.Preference.HasValue)
              rollingSumOfPreferences += PreferenceToInt(item.Preference.Value);
          else
              rollingSumOfPreferences += PreferenceAvg;
  
          // If we've hit or passed the random number, select this item
          if (rollingSumOfPreferences &gt;= number) {
              Result = item.ID;
              break;
          }
      }
  
      return Result;
  }
  
  private static int PreferenceToInt(float preference) {
      return (int)(Math.Pow(preference, 1.2) * 100);
  }
  </code></pre>