Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Random Order of an IEnumerable

I have got an IEnumerable collection as follows

var result1 = GetResult1() // Returns 2,4,5,6

I have to juggle the elements and create another collection in a random way which should result as follows:

var result2 = GetResult2(result1) // Returns 2,4,5,6 in a random order.
// An example output would be 4,6,2,5 in the resultant collection.

I have done this by the following means:

var result1 = GetResult1();
var random = new Random();
var result2 = result1.OrderBy(order=>random.Next());

However the issue with this is that if I access result2 the elements in result2 gets shuffled again, i.e if I output the results of result2 to a console twice, the elements are juggled again.

Can you please advice how to keep this uniform. i.e once I juggle the collection, it should remain the same way thereafter. I have to use lazy evaluation though, since the results are very huge in size.

like image 981
Mike Avatar asked Feb 20 '12 12:02

Mike


3 Answers

You are looking for a shuffle algorithm.

See following SO threads to provide nice extensions method to do the job:

Is using Random and OrderBy a good shuffle algorithm?

An extension method on IEnumerable needed for shuffling


To answer your question "why is that shuffled again?", it's because of how OrderBy works (lazy execution). You could try:

var result2 = result1.OrderBy(order=>random.Next()).ToList();
like image 123
ken2k Avatar answered Nov 13 '22 17:11

ken2k


I see you require lazy evaluation for the results, if that is the case, what you can do is this:

var randomNumbers = result1.Select(r => random.Next()).ToArray();
var orderedResult = result1.Zip(randomNumbers, (r, o) => new { Result = r, Order = o })
    .OrderBy(o => o.Order)
    .Select(o => o.Result);

By calling ToArray() on the random numbers, these will not change. When you finally desire the items in result1, you can zip the items with the random numbers, OrderBy the random number and Select the result.

As long as the items in result1 come in the same order, the result in orderedResult should be the same each time.

like image 33
Lukazoid Avatar answered Nov 13 '22 16:11

Lukazoid


You can use ToList() like this:

var result2 = result1.OrderBy(order=>random.Next()).ToList();
like image 2
soniiic Avatar answered Nov 13 '22 17:11

soniiic