Quel est le meilleur moyen d’obtenir une entrée aléatoire d’un dictionnaire en c #?
Le dictionnaire doit contenir plusieurs objets aléatoires à afficher sur une page. Toutefois, je ne peux pas utiliser les éléments suivants, car les dictionnaires ne sont pas accessibles par index:
Random Rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[Rand.Next()];
Aucune suggestion?
Mise à jour pour utiliser les génériques, être encore plus rapide et expliquer pourquoi cette option est plus rapide.
Cette réponse est similaire aux autres réponses, mais puisque vous avez dit que vous avez besoin de "plusieurs éléments aléatoires", cela sera plus performant:
public IEnumerable<TValue> RandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
Random Rand = new Random();
List<TValue> values = Enumerable.ToList(dict.Values);
int size = dict.Count;
while(true)
{
yield return values[Rand.Next(size)];
}
}
Vous pouvez utiliser cette méthode comme suit:
Dictionary<string, object> dict = GetDictionary();
foreach (object value in RandomValues(dict).Take(10))
{
Console.WriteLine(value);
}
Cela a amélioré les performances par rapport aux autres réponses (y compris la réponse de yshuditelu).
Mes tests montrent qu'avec 1000 objets dans le dictionnaire, cette méthode est environ 70 fois plus rapide que les autres méthodes suggérées.
Si vous utilisez .net 3.5, Enumerable a une méthode d’extension ElementAt qui vous permettrait de faire:
return dict.ElementAt(Rand.Next(0, dict.Count)).Value;
De votre dictionnaire ...
Dictionary<string, int> dict = new Dictionary<string, object>()
vous pouvez créer une liste complète de clés ...
List<string> keyList = new List<string>(dict.Keys);
et ensuite sélectionnez une clé aléatoire dans votre liste.
Random Rand = new Random();
string randomKey = keyList[Rand.Next(keyList.Count)];
Puis simplement retournez l'objet aléatoire correspondant à cette clé.
return dict[randomKey];
Mon autre réponse est correcte pour la question et serait utile dans de nombreux cas, comme obtenir des informations de jet de dés personnalisés (le jet de chaque dé est aléatoire, indépendant des autres dés). Cependant, vos commentaires donnent à penser que vous espériez peut-être obtenir une série d’éléments "uniques" dans la variable Dictionary
, un peu comme si vous distribuiez des cartes d’un jeu. Une fois qu'une carte est distribuée, vous ne voudrez plus jamais revoir la même carte tant que vous ne la mélangez plus. Dans ce cas, la meilleure stratégie dépendra de ce que vous faites.
Si vous ne récupérez que quelques éléments d'une Dictionary
large, vous devriez pouvoir adapter mon autre réponse en supprimant l'élément aléatoire de la liste à chaque fois qu'une nouvelle est récupérée. Vous voudrez probablement aussi faire de la liste une LinkedList
, car même s'il sera plus lent de trouver un élément par son index, il est beaucoup moins coûteux de supprimer des éléments au milieu de celui-ci. Le code pour cela serait un peu plus compliqué, donc si vous êtes prêt à sacrifier certaines performances pour plus de simplicité, vous pouvez simplement le faire:
public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
Random Rand = new Random();
Dictionary<TKey, TValue> values = new Dictionary<TKey, TValue>(dict);
while(values.Count > 0)
{
TKey randomKey = values.Keys.ElementAt(Rand.Next(0, values.Count)); // hat tip @yshuditelu
TValue randomValue = values[randomKey];
values.Remove(randomKey);
yield return randomValue;
}
}
Si, en revanche, vous prévoyez d'extraire un nombre important d'éléments de votre dictionnaire (c.-à-d. En distribuer plus que de vous connecter (n) à votre "deck"), vous ferez mieux de mélanger tout d'abord votre deck entier. , puis en tirant par le haut:
public IEnumerable<TValue> UniqueRandomValues<TKey, TValue>(IDictionary<TKey, TValue> dict)
{
// Put the values in random order
Random Rand = new Random();
LinkedList<TValue> values = new LinkedList<TValue>(from v in dict.Values
orderby Rand.Next()
select v);
// Remove the values one at a time
while(values.Count > 0)
{
yield return values.Last.Value;
values.RemoveLast();
}
}
Le crédit va à ookii.org pour le code de mélange simple. Si ce n'est toujours pas ce que vous cherchiez, vous pourriez peut-être poser une nouvelle question avec plus de détails sur ce que vous essayez de faire.
Quelque chose comme:
Random Rand = new Random();
Dictionary dict = GetDictionary();
var k = dict.Keys.ToList()[Rand.Next(dict.Count)];
return dict[k];
Ce ne sera pas très rapide, mais cela devrait marcher:
Random Rand = new Random();
Dictionary dict = GetDictionary();
return dict.Skip(Rand.Next(dict.Count)).First().Value;
Une solution simple consisterait à utiliser la méthode d'extension ToList()
et à utiliser l'index de la liste.
Si vous avez simplement besoin des valeurs ou des clés (pas la paire clé/valeur), renvoyez ces collections du dictionnaire et utilisez également ToList()
.
Random Rand = new Random();
Dictionary<string, object> dict = GetDictionary();
var k = dict.ToList()[Rand.Next(dict.Count)];
// var k = dict.Values.ToList()[Rand.Next(dict.Count)];
// var k = dict.Keys.ToList()[Rand.Next(dict.Count)];
Console.WriteLine("Random dict pair {0} = {1}", k.Key, k.Value);
Je crois que le seul moyen est de créer une liste séparée de KeyValuePairs en premier.
public static class DictionaryExtensions
{
public static TKey[] Shuffle<TKey, TValue>(
this System.Collections.Generic.Dictionary<TKey, TValue> source)
{
Random r = new Random();
TKey[] wviTKey = new TKey[source.Count];
source.Keys.CopyTo(wviTKey, 0);
for (int i = wviTKey.Length; i > 1; i--)
{
int k = r.Next(i);
TKey temp = wviTKey[k];
wviTKey[k] = wviTKey[i - 1];
wviTKey[i - 1] = temp;
}
return wviTKey;
}
}
// Using
System.Collections.Generic.Dictionary<object, object> myDictionary = new System.Collections.Generic.Dictionary<object, object>();
// myDictionary.Add(myObjectKey1, myObjectValue1); // Sample
// myDictionary.Add(myObjectKey2, myObjectValue2); // Sample
// myDictionary.Add(myObjectKey3, myObjectValue3); // Sample
// myDictionary.Add(myObjectKey4, myObjectValue4); // Sample
// var myShufledKeys = myDictionary.Shuffle(); // Sample
// var myShufledValue = myDictionary[myShufledKeys[0]]; // Sample
// Easy Sample
var myObjects = System.Linq.Enumerable.Range(0, 4);
foreach(int i in myObjects)
myDictionary.Add(i, string.Format("myValueObjectNumber: {0}", i));
var myShufledKeys = myDictionary.Shuffle();
var myShufledValue = myDictionary[myShufledKeys[0]];