2010-07-04 5 views
24

J'essaie d'obtenir un objet aléatoire dans linq. Voici comment je l'ai fait.Comment obtenir un objet aléatoire en utilisant Linq

//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++; 
} 

Est-ce la meilleure façon de procéder?

+2

Google m'a conduit à ce post, mais la réponse acceptée est trompeuse (en combinaison avec la question), car elle ne renvoie pas le premier et le dernier élément du tout (je suis conscient que cela pourrait être ce que vous cherchiez), mais étant donné que le titre de la question et la brève description concernent la recherche d'un élément aléatoire, je vous suggère de changer le libellé de la question (et/ou du titre) pour y réfléchir :) – Rouby

+0

@Rouby Je pense que vous avez suffisamment de rep pour changer – Luke101

Répondre

32

Qu'en est-:

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

Pour en savoir plus:

Les commentaires ci-dessous font de bonnes contributions à des questions étroitement liées, et je vais les inclure ici, puisque les points que @Rouby Les personnes cherchant une réponse à ces questions peuvent trouver cette réponse et cela ne sera pas correct dans ces cas.

Élément aléatoire À travers l'entrée entière

Pour tous les éléments d'un candidat dans la sélection aléatoire, vous devez changer l'entrée r.Next:

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

@Zidad ajoute une méthode d'extension utile obtenir un élément aléatoire sur tous les éléments de la séquence:

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)]; 
} 
+0

C'est génial..Laissez-moi demander où avez-vous appris C#? Quels livres as-tu lus? – Luke101

+0

Été si longtemps, je ne me souviens pas. Je code juste ces jours. Cependant, je recommande C# 4.0 par Joseph Albahari (http://www.albahari.com/nutshell/) et CLR via C# par Jeffrey Richter (http://www.amazon.com/CLR-Via-C-Pro -Developer/dp/0735621632). – codekaizen

+3

Oh, et je comprends qu'un gars du nom de Jon Skeet puisse savoir quelque chose sur C# et a écrit un livre à ce sujet. ;) – codekaizen

0

les réponses et les boucler ne sont pas la manière la plus efficace car vous déplacez beaucoup de données de la base de données. Si vous utilisez une clé primaire entière qui s'incrémente automatiquement, vous devriez obtenir le maximum de votre clé primaire, puis trouver l'entier aléatoire dans cette plage. Ensuite, obtenez directement la réponse unique basée sur la clé primaire dérivée de la fonction aléatoire.

+0

suppose un peu que l'on en particulier est toujours là, mais vous pouvez interroger pour la ligne qui a max (pk) <= choix –

+0

Bon point, il pourrait y avoir certains qui ont été supprimés ou lorsque le pk n'est pas monotones dans cette plage, probablement Le mieux est d'extraire la gamme d'une sous-requête pour s'assurer qu'elle existe. – Turnkey

7

Une autre approche loufoque (pas le plus efficace pour les ensembles de données plus importants):

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

cela est vraiment pris quelques thought..I ne pense pas que je l'aurais pensé à cette technique fascinante myself..very – Luke101

+0

je fais en lui donnant un OrderBy et une instance de hasard, mais NewGuid fonctionne aussi je suppose :) –

+0

Lorsque multi threading pour moi, il revient la même valeur. – CodeMilian

9

Utilisez un Fisher-Yates-Durstenfeld shuffle.

(Vous pouvez utiliser une méthode helper/extension shuffle your IEnumerable<T> sequence. Sinon, si vous utilisez un IList<T> vous pourriez perform an in-place shuffle, si vous préférez.)

+0

Vous pouvez faire ce shuffle sans un autre tableau en démarrant l'index à la fin et en sélectionnant aléatoirement un index des éléments avant l'index actuel, et en échangeant les valeurs. Cela continue dans une boucle, en décrémentant l'index actuel, jusqu'à ce que le début soit atteint. – codekaizen

+1

@codekaizen: Vous pouvez le faire si votre séquence est indexable (par exemple, si c'est vraiment un '' IList de quelque sorte). Vous ne pouvez pas garantir que tout 'IEnumerable ' arbitraire sera indexable. – LukeH

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

Idéalement, vous voulez que jamais faire fonctionner requête pour une seule valeur, donc vous configurez le Skip/Take pour sauter au numéro de séquence correspondant au nombre aléatoire que vous générez (délimité par itemcount de l'ensemble de données, donc la limite de problème de ligne manquante basée sur MAX (pkey) n'est pas un problème) et ensuite accrocher le premier élément à ce moment de la séquence.

Dans SQL cela est la même chose que pour l'interrogation SELECT Count(*) FROM q, puis SELECT * FROM q LIMIT {0}, 1{0} est rand.Next(0, count), ce qui devrait être assez efficace.

4

méthode d'extension générique basée sur la réponse acceptée (qui ne saute pas toujours le premier, et énumère seulement dénombrable fois):

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())); 
     } 
    } 
+0

Nice et soignée, cependant la ligne 'var r = new Random();' devrait être déplacé pour devenir un membre de classe statique. Sinon, les appels rapides à la méthode génèreront le même résultat. –

+1

Aha merci pour cela. Dans mon cas, retourner accidentellement le même nombre aléatoire au cas où ils sont exécutés exactement au même moment n'est pas un gros problème. Cependant méfiez-vous d'une instance aléatoire Random parce que Next() n'est pas thread-safe et pourrait casser. [ThreadStatic] pourrait fonctionner ou d'un thread-safe telle solution aléatoire(): http://stackoverflow.com/a/11109361/67910 –

0

Je table produits dans la base de données, chaque utilisateur de temps entre un produit détail Je veux montrer 10 produits similaires en dessous de la page. Et dans chaque rafraîchissement cette liste doit être changer. Il doit venir au hasard.

Linq ressemble à ce

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

x.Id!=id 

ce que pour ne pas mettre le produit sélectionné à la liste.

Il fonctionne parfaitement

1

je poste une réponse parce que je n'ai pas assez réputation pour commenter.

J'aime cette réponse:

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

Mais ElementAt est nul base, en commençant sans doute à 1 et va Answers.Count(), vous allez finir par jeter potentiellement une hors de portée, et vous êtes ne va jamais obtenir la première entité.

Ne serait-

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

mieux?

+1

La [limite supérieure à 'Next' est exclusive] (http://msdn.microsoft. com/en-us/bibliothèque/2dx6wyd4% 28v = vs.110% 29.aspx) ce qui signifie que vous manquez maintenant le dernier élément. Correct serait 'suivante (0, Answers.Count())' – ChrisWue

+0

Notez également la question était aléatoire * après la première *, donc le début à l'index 1. – codekaizen

+1

Je ne ai jamais réalisé le hasard suivant était exclusive supérieure, semble un peu débile IMHO . Je ne vois nulle part dans la première question toute mention de miss en premier, sinon que c'est ce que son code ferait, mais cela ne veut pas dire qu'il devrait le faire. – Tod

0

En retard à la fête mais c'est un résultat élevé de Google. Une version succincte pourrait être:

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

Il a l'inconvénient qu'il va appliquer un nombre aléatoire à tous les éléments, mais est compact et peut facilement être modifié pour prendre plus d'un élément aléatoire.

Questions connexes