Подтвердить что ты не робот

Случайная запись из словаря

Каков наилучший способ получить случайную запись из Словаря в С#?

Мне нужно получить несколько случайных объектов из fictionary для отображения на странице, однако я не могу использовать:

Random rand = new Random();
Dictionary< string, object> dict = GetDictionary();
return dict[rand.Next()];

поскольку словари не могут получить доступ по индексу.

Любые предложения?

4b9b3361

Ответ 1

Обновлен, чтобы использовать generics, быть еще быстрее и объяснять, почему эта опция выполняется быстрее.

Этот ответ аналогичен другим ответам, но поскольку вы сказали, что вам нужно "количество случайных элементов", это будет более результативным:

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

Вы можете использовать этот метод следующим образом:

Dictionary<string, object> dict = GetDictionary();
foreach (object value in RandomValues(dict).Take(10))
{
    Console.WriteLine(value);
}

Это улучшает производительность по сравнению с другими ответами (включая ответ yshuditelu).

  • Ему не нужно создавать новую коллекцию всех элементов словаря каждый раз, когда вы хотите получить новое случайное значение. Это действительно большая сделка, если в вашем словаре есть много элементов.
  • Он не должен выполнять поиск на основе ключа словаря каждый раз, когда вы выбираете случайное значение. Не такая большая сделка, как №1, но она по-прежнему в два раза быстрее.

Мои тесты показывают, что с 1000 объектами в словаре этот метод идет примерно в 70 раз быстрее, чем другие предлагаемые методы.

Ответ 2

Если вы используете .net 3.5, Enumerable имеет метод расширения ElementAt, который позволит вам сделать:

return dict.ElementAt(rand.Next(0, dict.Count)).Value;

Ответ 3

Из вашего словаря...

Dictionary<string, int> dict = new Dictionary<string, object>()

вы можете создать полный список ключей...

List<string> keyList = new List<string>(dict.Keys);

а затем выберите случайный ключ из списка.

Random rand = new Random();
string randomKey = keyList[rand.Next(keyList.Count)];

Затем просто возвращает случайный объект, соответствующий этому ключу.

return dict[randomKey];

Ответ 4

Мой другой ответ правильный для вопроса и будет полезен во многих случаях, например, получение информации о роли из пользовательских кубиков (каждый бросок кубика случайный, независимо от других кубиков). Тем не менее, ваши комментарии звучат так, как будто вы можете надеяться получить серию "уникальных" элементов из Dictionary, вроде как раздавать карты из колоды. После раздачи карты вам никогда не захочется видеть ту же самую карту снова, пока вы не перетасовываете ее. В этом случае лучшая стратегия будет зависеть именно от того, что вы делаете.

Если вы получаете только несколько элементов из большого Dictionary, вы должны уметь адаптировать мой другой ответ, удаляя случайный элемент из списка каждый раз, когда извлекается новый. Вероятно, вы также захотите сделать список в LinkedList, потому что хотя он будет медленнее найти элемент по его индексу, гораздо дешевле удалить элементы из середины. Код для этого был бы немного сложнее, поэтому, если вы хотите пожертвовать некоторой производительностью для простоты, вы можете просто сделать это:

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

Если, с другой стороны, вы планируете вытаскивать значительное количество элементов из вашего словаря (т.е. обрабатывать больше, чем log (n) вашей "колоды" ), вам будет лучше просто перетасовать ваш сначала целую колоду, а затем потянув сверху:

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

Кредит переходит на ookii.org для простого кода перетасовки. Если это еще не совсем то, что вы искали, возможно, вы можете начать новый вопрос с более подробной информацией о том, что вы пытаетесь сделать.

Ответ 5

Что-то вроде:

Random rand = new Random();
Dictionary dict = GetDictionary();
var k = dict.Keys.ToList()[rand.Next(dict.Count)];
return dict[k];

Ответ 6

Это не будет ужасно быстро, но он должен работать:

Random rand = new Random();
Dictionary dict = GetDictionary();
return dict.Skip(rand.Next(dict.Count)).First().Value;

Ответ 7

Легким решением было бы использовать метод расширения ToList() и использовать индекс списка.

Если вам нужны только значения или ключи (а не пара ключ/значение), верните эти коллекции из словаря и используйте 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);

Ответ 8

Я считаю, что единственный способ - создать отдельный список KeyValuePairs.

Ответ 9

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]];