13

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

Chris Marisic
  • 32,487
  • 24
  • 164
  • 258
Marco Casiraghi
  • 461
  • 7
  • 19
  • Are you saying that you want the entire list randomized and then you want to retrieve the first 50 items? – Ben Gribaudo Jul 26 '10 at 21:45
  • This question does not duplicate the linked question. This question specifically denotes ASP.NET which requires ThreadSafe random access. The linked question makes no mention of ThreadSafe – Chris Marisic Sep 17 '14 at 17:30

4 Answers4

33

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.

Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
2

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
  • 31,211
  • 14
  • 77
  • 99
1
Random random = new Random();
return _repositoryKeyWord.FindAll(x => x.Category.Id == idCAtegory)
                .OrderBy(x => r.Next())
                .Take(50).ToList();
Yuriy Faktorovich
  • 67,283
  • 14
  • 105
  • 142
  • 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 Jul 26 '10 at 21:48
  • 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 Jul 26 '10 at 21:50
  • 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 Jul 26 '10 at 21:58
  • @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 Jul 26 '10 at 22:01
1

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;
    }
}
Evan Trimboli
  • 29,900
  • 6
  • 45
  • 66
Rei Miyasaka
  • 7,007
  • 6
  • 42
  • 69