tags:

views:

74

answers:

4

I am trying to get a random object within linq. Here is how I did.

            //get all the answers
            var Answers = q.Skip(1).Take(int.MaxValue);

            //get the random number by the number of answers
            int intRandomAnswer = r.Next(1, Answers.Count());
            int count = 0;

            //locate the answer
            foreach(var Answer in Answers)
            {
                if (count == intRandomAnswer)
                {
                    SelectedPost = Answer;
                    break;
                }
                count++;
            }

Is this the best way to do this?

+3  A: 

What about:

SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));
codekaizen
This is awesome..Let me ask where did you learn c#? What books did you read?
Luke101
Been so long, I don't remember. I just code these days. However, I'd recommend C# 4.0 by Joseph Albahari (http://www.albahari.com/nutshell/) and CLR via C# by Jeffrey Richter (http://www.amazon.com/CLR-Via-C-Pro-Developer/dp/0735621632).
codekaizen
Oh, and I understand some guy by the name of Jon Skeet might know a thing or two about C# and wrote a book about it. ;)
codekaizen
A: 

Pulling all of the answers and looping them isn't the most efficient way as you're moving lots of data from the database. If you're using an integer primary key that's automatically incrementing, you should get the Max of your primary key and then find the random integer within that range. Then directly get the single answer based on the primary key derived from the random function.

Turnkey
somewhat assumes that particular one is still around, but you could query for the row that has max(pk) <= choice
James Manning
Good point, there could be some that have been deleted or where the pk is not monotonic within that range, probably best to get the range out of a subquery to ensure that it exists.
Turnkey
+1  A: 

Another wacky approach (not the most efficient for larger data sets):

SelectedPost = q.OrderBy(qu => Guid.NewGuid()).First();
BFree
this is really took some thought..I dont think I would have thought of this technique myself..very fascinating
Luke101
i typically do it with an OrderBy and an instance of Random, but NewGuid works too I guess :)
James Manning
+1  A: 

Use a Fisher-Yates-Durstenfeld shuffle. Here's a general purpose extension method that does just that:

// use First for a single random answer or Take(n) for multiple answers
var SelectedPost = Answers.Shuffle().First();

// ...

public static class EnumerableExtensions
{
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> source)
    {
        // error checking etc removed for brevity

        Random rng = new Random();
        T[] sourceArray = source.ToArray();

        for (int n = 0; n < sourceArray.Length; n++)
        {
            int k = rng.Next(n, sourceArray.Length);
            yield return sourceArray[k];

            sourceArray[k] = sourceArray[n];
        }
    }
}
LukeH