web-dev-qa-db-fra.com

Comment obtenir un objet aléatoire avec Linq

J'essaie d'obtenir un objet aléatoire dans linq. Voici comment j'ai fait. 

//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 faire cela?

26
Luke101

Qu'en est-il de:

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

Lectures supplémentaires:

Les commentaires ci-dessous apportent de bonnes contributions à des questions étroitement liées, et je les inclurai ici car, comme @Rouby le fait remarquer, les personnes à la recherche d'une réponse à cette question pourraient trouver cette réponse, qui ne sera pas correcte dans ces cas.

Elément aléatoire sur toute l'entrée

Pour que tous les éléments soient candidats à la sélection aléatoire, vous devez modifier l’entrée en r.Next:

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

@Zidad ajoute une méthode d'extension utile pour 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)];
}
36
codekaizen

Utilisez un Fisher-Yates-Durstenfeld shuffle .

(Vous pouvez utiliser une méthode d'assistance/extension pour mélanger votre séquence IEnumerable<T> . Sinon, si vous utilisiez un IList<T>, vous pourriez effectuer un mélange aléatoire , si vous préférez.)

9
LukeH

Une autre approche délirante (pas la plus efficace pour les grands ensembles de données):

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

Méthode d'extension générique basée sur la réponse acceptée (qui ne saute pas toujours la première et n'énumère qu'une fois l'énumérable):

 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()));
        }
    }
4
Wiebe Tijsma
var Rand = new Random();
var SelectedPost = q.Skip(Rand.Next(0,q.Count)).Take(1);

De manière optimale, vous souhaitez que la fonction ne soit interrogée que sur une seule valeur. Par conséquent, vous configurez l'option Ignorer/Prendre pour qu'elle passe au numéro de séquence correspondant au nombre aléatoire généré (lié par le nombre d'éléments de l'ensemble de données, de sorte la limite basée sur MAX (pkey) n’est pas un problème), puis attrapez le premier élément à cet endroit de la séquence.

En SQL, cela revient à interroger SELECT Count(*) FROM q, puis SELECT * FROM q LIMIT {0}, 1, où {0} est Rand.Next(0, count), ce qui devrait être assez efficace.

3
Garandy

En retard pour la fête, mais il s'agit d'un résultat élevé de Google. Une version succincte pourrait être:

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

L’inconvénient est qu’elle appliquera un nombre aléatoire à tous les éléments, mais elle est compacte et peut facilement être modifiée pour prendre plus d’un élément aléatoire.

2
Dan

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

J'aime cette réponse:

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

Mais ElementAt est basé sur zéro, démarrant sûrement à 1 et allant à Answers.Count (), vous allez éventuellement jeter un hors de portée, et vous n'obtiendrez jamais la première entité.

Ne serait pas

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

Être meilleur?

1
Tod

Extraire toutes les réponses et les boucler n'est pas la méthode 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 devez obtenir le maximum de votre clé primaire, puis rechercher le nombre 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
Turnkey

J'ai la table des produits dans la base de données, chaque fois que l'utilisateur entre un détail de produit, je souhaite afficher 10 produits similaires ci-dessous en page.Et dans chaque actualisation, cette liste doit être modifiée. Elle doit venir de manière aléatoire.

Linq ressemble à ceci

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

x.Id!=id 

ceci uniquement pour ne pas mettre le produit sélectionné à la liste.

Ça marche parfaitement 

0
RASKOLNIKOV