Luke101
Luke101

Reputation: 65268

How to get a Random Object using Linq

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?

Upvotes: 38

Views: 51385

Answers (8)

Garandy
Garandy

Reputation: 203

var rand = new Random();
var selectedPost = q.Skip(rand.Next(0, q.Count())).Take(1).FirstOrDefault();

Optimally, you want to only ever make the function query for a single value, so you set up the Skip/Take to jump up to the sequence number matching the random number you're generating (bounded by dataset's itemcount, so the missing row problem bounding based on MAX(pkey) isn't an issue) and then snag the first item at that point in the sequence.

In SQL this is the same as querying for SELECT Count(*) FROM q, then SELECT * FROM q LIMIT {0}, 1 where {0} is rand.Next(0, count), which should be pretty efficient.

Upvotes: 4

Dan
Dan

Reputation: 1192

Late to the party but this is a high-up Google result. A succinct version could be:

var rnd = new Random();
var SelectedPost = q.OrderBy(x => rnd.Next()).Take(1);

It has the disadvantage that it'll apply a random number to all elements, but is compact and could easily be modified to take more than one random element.

Upvotes: 5

LukeH
LukeH

Reputation: 269398

Use a Fisher-Yates-Durstenfeld shuffle.

(You could use a helper/extension method to shuffle your IEnumerable<T> sequence. Alternatively, if you were using an IList<T> you could perform an in-place shuffle, if you prefer.)

Upvotes: 11

codekaizen
codekaizen

Reputation: 27419

What about:

SelectedPost = q.ElementAt(r.Next(1, Answers.Count()));

Further reading:

The comments below make good contributions to closely related questions, and I'll include them here, since as @Rouby points out, people searching for an answer to these may find this answer and it won't be correct in those cases.

Random Element Across Entire Input

To make all elements a candidate in the random selection, you need to change the input to r.Next:

SelectedPost = Answers.ElementAt(r.Next(0, Answers.Count()));

@Zidad adds a helpful extension method to get random element over all elements in the sequence:

public static T Random<T>(this IEnumerable<T> enumerable)
{
    if (enumerable == null)
    {
         throw new ArgumentNullException(nameof(enumerable));
    }

    // note: creating a Random instance each call may not be correct for you,
    // consider a thread-safe static instance
    var r = new Random();  
    var list = enumerable as IList<T> ?? enumerable.ToList(); 
    return list.Count == 0 ? default(T) : list[r.Next(0, list.Count)];
}

Upvotes: 54

RASKOLNIKOV
RASKOLNIKOV

Reputation: 748

I have product table in database ,every time user enters one product detail I want to show 10 similar products in below of page.And in every refresh this list must be change .it must come randomly.

Linq looks like this

var products =
            DataContextFactory.GetDataContext()
                .Set<Product>()
                .Where(x =>x.Id!=id)
                .OrderBy(emp => Guid.NewGuid())
                .Take(10).ToList();

x.Id!=id 

this only for not put selected product to list .

It works perfect

Upvotes: 0

Wiebe Tijsma
Wiebe Tijsma

Reputation: 10265

Generic extension method based on the accepted answer (which doesn't always skip the first, and only enumerates the enumerable once):

 public static class EnumerableExtensions
    {
        public static T Random<T>(this IEnumerable<T> enumerable)
        {
            var r = new Random();
            var list = enumerable as IList<T> ?? enumerable.ToList();
            return list.ElementAt(r.Next(0, list.Count()));
        }
    }

Upvotes: 3

BFree
BFree

Reputation: 103740

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

SelectedPost = q.OrderBy(qu => Guid.NewGuid()).First();

Upvotes: 15

Turnkey
Turnkey

Reputation: 9406

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.

Upvotes: 2

Related Questions