Linq2sql: efficient way to get random elements with weight?

c#, linq-to-sql, random

Solution

My first idea was the same as Ron Klein's - create a weighted list and select randomly from that.

Here's a LINQ extension method to create the weighted list from the normal list, given a lambda function that knows the weight property of the object.

Don't worry if you don't get all the generics stuff right away... The usage below should make it clearer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    public class Item
    {
        public int Weight { get; set; }
        public string Name { get; set; }
    }

    public static class Extensions
    {
        public static IEnumerable<T> Weighted<T>(this IEnumerable<T> list, Func<T, int> weight)
        {
            foreach (T t in list)
                for (int i = 0; i < weight(t); i++)
                    yield return t;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            List<Item> list = new List<Item>();
            list.Add(new Item { Name = "one", Weight = 5 });
            list.Add(new Item { Name = "two", Weight = 1 });

            Random rand = new Random(0);

            list = list.Weighted<Item>(x => x.Weight).ToList();

            for (int i = 0; i < 20; i++)
            {
                int index = rand.Next(list.Count());
                Console.WriteLine(list.ElementAt(index).Name);
            }

            Console.ReadLine();
        }
    }
}

As you can see from the output, the results are both random and weighted as you require.

Problem

Byt lets say I have an integer weight where i.e. elements with weight 10 has 10 times higher probability to be selected than element with weight 1. ``` var ws = db.WorkTypes .Where(e => e.HumanId != null && e.SeoPriority != 0) .OrderBy(e => /*????*/ * e.SeoPriority) .Select(e => new { DescriptionText = e.DescriptionText, HumanId = e.HumanId }) .Take(take).ToArray(); ``` How do I solved getting random records in Linq when I need the result to be weighted? I need somthing like Random Weighted Choice in T-SQL but in linq and not only getting one record? If I wouldn't have the weighted requirement, I'd use the NEWID approach, can I adopt this some way? ``` partial class DataContext { [Function(Name = "NEWID", IsComposable = true)] public Guid Random() { throw new NotImplementedException(); } } ... var ws = db.WorkTypes .Where(e => e.HumanId != null && e.SeoPriority != 0) .OrderBy(e => db.Random()) .Select(e => new { DescriptionText = e.DescriptionText, HumanId = e.HumanId }) .Take(take).ToArray(); ```

Original source