views:

140

answers:

6

i'm using Asp.net MVC with Sharp Architecture.

I have this code:

return _repositoryKeyWord.FindAll(x => x.Category.Id == idCAtegory)
                .Take(50).ToList();

How can i order by random? Note: i don't want to order the 50 extracted items, i want order before and then extract 50 items.

thks

A: 
Random random = new Random();
return _repositoryKeyWord.FindAll(x => x.Category.Id == idCAtegory)
                .OrderBy(x => r.Next())
                .Take(50).ToList();
Yuriy Faktorovich
Hah, I like that.If it's using quick sort though, that might crash. I know that trying to use Array.Sort on an inconsistent parameter will cause an exception.
Rei Miyasaka
I'd like to see the sql that generates. Is it returning the entire table, then sorting and throwing away everything but the top 50 records?
Gabe Moothart
Ah, yeah, that should be .Take(50).OrderBy(x => r.Next()), not the other way around. Edit: never mind, I misread the question.
Rei Miyasaka
@Rei Miyasaka: Why, he specifically states he doesn't want to sort just the 50 items? Does the SQL of reversing them generate correctly according to spec?
Yuriy Faktorovich
+2  A: 

Probably best to write your own extension method to do it.

 public static class Extensions
{
    static readonly Random random = new Random();
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)
    {
        return Shuffle(items, random);
    }
    public static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items, Random random)
    {
        // Un-optimized algorithm taken from
        // http://en.wikipedia.org/wiki/Knuth_shuffle#The_modern_algorithm
        List<T> list = new List<T>(items);
        for (int i = list.Count - 1; i >= 1; i++)
        {
            int j = random.Next(0, i);
            T temp = list[i];
            list[i] = list[j];
            list[j] = temp;
        }
        return list;
    }
}

Edit: Be sure to do .Take(50).Shuffle() rather than .Shuffle().Take(50); otherwise, at best it'll crash and at worst it'll take forever.

Edit 2: Never mind, this won't work if you want to query a random set of 50.

Rei Miyasaka
+2  A: 

You can do this in T-Sql as described here. I don't think you can do it in linq without loading the whole result set into memory and then throwing most of it away, which you do not want to do.

Gabe Moothart
+7  A: 

One way to achieve efficiently is to add a column to your data Shuffle that is populated with a random int (as each record is created).

The query to access the table then becomes ...

Random random = new Random();
int seed = random.Next();
result = result.OrderBy(s => (~(s.Shuffle & seed)) & (s.Shuffle | seed)); // ^ seed);

This does an XOR operation in the database and orders by the results of that XOR.

Advantages:-

  1. Efficient: SQL handles the ordering, no need to fetch the whole table
  2. Repeatable: (good for testing) - can use the same random seed to generate the same random order
  3. Works on most (all?) Entity Framework supported databases

This is the approach used by my home automation system to randomize playlists. It picks a new seed each day giving a consistent order during the day (allowing easy pause / resume capabilities) but a fresh look at each playlist each new day.

Hightechrider
This solution made me go ooooo and ahhhh.
jfar
jfar: Me too; 'tis pwesome. Although I just used the PK (which is an int) instead of a Shuffle field and it works just fine. Thanks Hightechrider!
Frank Tzanabetis
A: 

How about this?

return _repositoryKeyWord.FindAll(x => x.Category.Id == idCAtegory)
  .OrderBy (x=> new Guid())
  .Take(50).ToList();
Dan Atkinson
+1  A: 

Duplicated question http://stackoverflow.com/questions/648196/random-row-from-linq-to-sql

Dennis Cheung