elementat - c# dictionary initialization
Entrada aleatoria del diccionario (9)
¿Cuál es la mejor manera de obtener una entrada aleatoria de un diccionario en c #?
Necesito obtener una cantidad de objetos aleatorios del diccionario para mostrarlos en una página; sin embargo, no puedo usar los siguientes porque no se puede acceder a los diccionarios por índice:
Random rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[rand.Next()];
¿Alguna sugerencia?
Algo como:
Random rand = new Random();
Dictionary dict = GetDictionary();
var k = dict.Keys.ToList()[rand.Next(dict.Count)];
return dict[k];
Creo que la única manera es crear una lista separada de KeyValuePairs primero.
De tu diccionario ...
Dictionary<string, int> dict = new Dictionary<string, object>()
puedes crear una lista completa de llaves ...
List<string> keyList = new List<string>(dict.Keys);
y luego seleccione una clave aleatoria de su lista.
Random rand = new Random();
string randomKey = keyList[rand.Next(keyList.Count)];
Luego simplemente devuelve el objeto aleatorio que coincida con esa clave.
return dict[randomKey];
Esto no será terriblemente rápido, pero debería funcionar:
Random rand = new Random();
Dictionary dict = GetDictionary();
return dict.Skip(rand.Next(dict.Count)).First().Value;
Mi otra respuesta es correcta para la pregunta, y sería útil en muchos casos, como obtener información de los dados personalizados (la tirada de cada dado es aleatoria, independiente de los otros dados). Sin embargo, sus comentarios lo hacen parecer como si estuviera esperando obtener una serie de elementos "únicos" del Dictionary
, algo así como repartir cartas desde un mazo. Una vez que se reparte una carta, no querrá volver a ver la misma carta hasta que se vuelva a mezclar. En ese caso, la mejor estrategia dependerá exactamente de lo que esté haciendo.
Si solo obtiene algunos elementos de un Dictionary
grande, entonces debería poder adaptar mi otra respuesta, eliminando el elemento aleatorio de la lista cada vez que se recupera uno nuevo. Probablemente también quiera hacer la lista en LinkedList
, porque aunque será más lento encontrar un elemento por su índice, es mucho menos costoso eliminar elementos de la mitad. El código para esto sería un poco más complicado, así que si estás dispuesto a sacrificar algo de rendimiento por simplicidad, puedes hacer esto:
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, por otro lado, está planeando extraer un número significativo de elementos de su diccionario (es decir, distribuyendo más que el registro (n) de su "mazo"), será mejor que solo baraje todo su mazo primero , y luego tirando de la parte superior:
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();
}
}
El crédito va a ookii.org por el simple código de mezcla. Si todavía no es lo que estabas buscando, quizás puedas comenzar una nueva pregunta con más detalles sobre lo que estás tratando de hacer.
Si está utilizando .net 3.5, Enumerable tiene un método de extensión ElementAt que le permite hacer:
return dict.ElementAt(rand.Next(0, dict.Count)).Value;
Una solución fácil sería usar el método de extensión ToList()
y usar el índice de la lista.
Si solo necesita los valores o las claves (no el par de clave / valor), devuelva estas colecciones del diccionario y use ToList()
también.
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);
Actualizado para usar genéricos, sea aún más rápido y con una explicación de por qué esta opción es más rápida.
Esta respuesta es similar a las otras respuestas, pero como dijiste que necesitas "una cantidad de elementos aleatorios", esta será más eficiente:
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)];
}
}
Puedes usar este método así:
Dictionary<string, object> dict = GetDictionary();
foreach (object value in RandomValues(dict).Take(10))
{
Console.WriteLine(value);
}
Esto tiene mejoras de rendimiento sobre las otras respuestas (incluida la respuesta de yshuditelu).
- No tiene que crear una nueva colección de todos los elementos del diccionario cada vez que desee obtener un nuevo valor aleatorio. Esto es realmente importante si tu diccionario tiene muchos elementos.
- No tiene que realizar una búsqueda basada en la clave del diccionario cada vez que obtiene un valor aleatorio. No es tan importante como el n. ° 1, pero sigue siendo el doble de rápido de esta manera.
Mis pruebas muestran que con 1000 objetos en el diccionario, este método es 70 veces más rápido que los otros métodos sugeridos.
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;
}
}
Muestra
// 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]];