195

How can I get a random pair from a dict? I'm making a game where you need to guess a capital of a country and I need questions to appear randomly.

The dict looks like {'VENEZUELA':'CARACAS'}

How can I do this?

martineau
  • 112,593
  • 23
  • 157
  • 280
tekknolagi
  • 10,089
  • 22
  • 71
  • 114

19 Answers19

338

One way would be:

import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'OTTAWA'}
random.choice(list(d.values()))

EDIT: The question was changed a couple years after the original post, and now asks for a pair, rather than a single item. The final line should now be:

country, capital = random.choice(list(d.items()))
Nathaniel Jones
  • 776
  • 1
  • 8
  • 22
Gerrat
  • 27,594
  • 8
  • 70
  • 98
  • 1
    To expand on Duncan's comment, you can also just do `random.choice([d.keys()])`. The brackets convert the iterator to a list just the same as calling `list()`, but it may look a bit cleaner for some. – Seaux Jul 14 '15 at 15:48
  • 16
    Is this efficient? Does this scale if dict gets really large? –  Jul 25 '15 at 19:11
  • 1
    @code-法: Depends on what you mean by *efficient*, and *large*. A quick test shows it can pick a million random values out of a dictionary with a million entries in less than a second (assumes saving off the `d.keys()` part). Is that efficient? – Gerrat Aug 11 '15 at 20:44
  • @Gerrat, do you know if the transformation of d.keys() from iterator to list if constant (k) or O(n)? – toto_tico Sep 20 '15 at 15:11
  • 2
    @toto_tico: Changing an iterator to a list would be O(n) – Gerrat Sep 21 '15 at 15:30
  • @Seaux: brackets just make a one-item list whose item is the iterator itself. They don't make a list whose items are the iterator's items. – Bruno Le Floch Dec 07 '16 at 14:43
  • 1
    @austingae: because `random.choice` takes sequence as the parameter (see [Sequence Types](https://docs.python.org/library/stdtypes.html#sequence-types-list-tuple-range)) and dictionary isn't a sequence - you can't refer to its elements by index. – jack1142 Feb 19 '19 at 23:40
  • Careful: this answer does not seem to apply for complex keys (tuples, lists, and possibly other iterables). In that case: `index = random.choice(range(len(d)))` and work from there (remember order is only garantueed in OrderedDict). – David. Jan 10 '20 at 14:23
22

I wrote this trying to solve the same problem:

https://github.com/robtandy/randomdict

It has O(1) random access to keys, values, and items.

Rob T
  • 391
  • 4
  • 3
12

If you don't want to use the random module, you can also try popitem():

>> d = {'a': 1, 'b': 5, 'c': 7}
>>> d.popitem()
('a', 1)
>>> d
{'c': 7, 'b': 5}
>>> d.popitem()
('c', 7)

Since the dict doesn't preserve order, by using popitem you get items in an arbitrary (but not strictly random) order from it.

Also keep in mind that popitem removes the key-value pair from dictionary, as stated in the docs.

popitem() is useful to destructively iterate over a dictionary

Community
  • 1
  • 1
patriciasz
  • 1,277
  • 9
  • 10
  • 34
    This is kind of bad, because if you load the dictionary in the same order on the same version of python you'll almost certainly get the same item. – Alex May 22 '14 at 17:43
  • See also: http://stackoverflow.com/questions/10593651/pythonic-way-to-access-arbitrary-element-from-dictionary – Ciro Santilli Путлер Капут 六四事 Nov 11 '16 at 11:01
  • 38
    python 3.6 dict is actually *ORDERED*! This is super important – Or Duan Jan 30 '17 at 16:01
  • 11
    This answer is just wrong. Even before 3.6 this will not give you a random value, but a value depending on the hash value ot the item. – OBu Jun 25 '18 at 20:27
  • 2
    Ignoring the incongruity with the OP's request, this way is a good way to get over the fact that `next` does not work with a dictionary as it is a mapping. (I came here for inspiration and this was it). – Matteo Ferla Nov 13 '18 at 10:19
  • `popitem()` would only return a random item, when you `shuffle` the `dict` first or you are somehow sure that it is already in random order. – phi Dec 02 '21 at 16:21
11

Try this:

import random
a = dict(....) # a is some dictionary
random_key = random.sample(a, 1)[0]

This definitely works.

lavee_singh
  • 1,303
  • 1
  • 13
  • 21
9
>>> import random
>>> d = dict(Venezuela = 1, Spain = 2, USA = 3, Italy = 4)
>>> random.choice(d.keys())
'Venezuela'
>>> random.choice(d.keys())
'USA'

By calling random.choice on the keys of the dictionary (the countries).

user225312
  • 118,119
  • 66
  • 167
  • 181
  • 4
    Hi, I try to use your method to randomly pick one key from a given dictionary, but it does not work. My dictionary looks like a = {'walk': 20, 'run': 80} and when I use random.choice(a.keys()), there is an error like "dict_keys' object does not support indexing". Do you know what's wrong? Thanks a lot!!! – beepretty May 11 '16 at 04:50
  • 9
    @beepretty dict.keys() returns an iterable object, not a list-like object. To resolve the error, use `random.choice(list(d.keys()))`. – Greenstick Mar 23 '17 at 04:51
  • 3
    Use random.sample instead, which is cheaper than copying the keys to a list. – steveayre Aug 09 '18 at 13:35
  • @steveayre No, `random.sample` doesn't work with dict input. – wim Oct 29 '20 at 06:23
8

This works in Python 2 and Python 3:

A random key:

random.choice(list(d.keys()))

A random value

random.choice(list(d.values()))

A random key and value

random.choice(list(d.items()))
firelynx
  • 28,634
  • 8
  • 83
  • 95
5

Since the original post wanted the pair:

import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'}
country, capital = random.choice(list(d.items()))

(python 3 style)

OBu
  • 4,661
  • 2
  • 26
  • 43
3

If you don't want to use random.choice() you can try this way:

>>> list(myDictionary)[i]
'VENEZUELA'
>>> myDictionary = {'VENEZUELA':'CARACAS', 'IRAN' : 'TEHRAN'}
>>> import random
>>> i = random.randint(0, len(myDictionary) - 1)
>>> myDictionary[list(myDictionary)[i]]
'TEHRAN'
>>> list(myDictionary)[i]
'IRAN'
  • 1
    Pay attention: you generate a `list(…)` here multiple times. How about `myDictionary_list = list(myDictionary)`? – Cadoiz Jul 10 '20 at 23:05
2

I am assuming that you are making a quiz kind of application. For this kind of application I have written a function which is as follows:

def shuffle(q):
"""
The input of the function will 
be the dictionary of the question
and answers. The output will
be a random question with answer
"""
selected_keys = []
i = 0
while i < len(q):
    current_selection = random.choice(q.keys())
    if current_selection not in selected_keys:
        selected_keys.append(current_selection)
        i = i+1
        print(current_selection+'? '+str(q[current_selection]))

If I will give the input of questions = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'} and call the function shuffle(questions) Then the output will be as follows:

VENEZUELA? CARACAS
CANADA? TORONTO

You can extend this further more by shuffling the options also

Martijn Pieters
  • 963,270
  • 265
  • 3,804
  • 3,187
Anivarth
  • 523
  • 4
  • 19
  • 1
    You should not program drawing without replacement like this. Imagine drawing the 100th of 100 elements - 99% chance to hit an element inside of `selected_keys`. And you will likely repeat that about 100 times. At least delete the taken keys of q like `del(q[current_selection])` - you can overwrite q with the new shuffled `selected_keys` just like `q = selected_keys` after everything. Also don't forget about identing! Alternatively look at my `np.random.choice(...)` approach: https://stackoverflow.com/a/62843377/4575793 – Cadoiz Jul 10 '20 at 23:01
2

Since this is homework:

Check out random.sample() which will select and return a random element from an list. You can get a list of dictionary keys with dict.keys() and a list of dictionary values with dict.values().

carl
  • 48,340
  • 17
  • 72
  • 81
  • 5
    `random.sample` and `random.choice` can't work with iterators. They need to know the length of the sequence, which can't be determined from an iterator. – AFoglia Feb 01 '11 at 05:50
  • 4
    `random.sample` returns `k` random _elements_, `random.choice` returns a single random element – Anentropic Jan 30 '14 at 13:05
2

With modern versions of Python(since 3), the objects returned by methods dict.keys(), dict.values() and dict.items() are view objects*. And hey can be iterated, so using directly random.choice is not possible as now they are not a list or set.

One option is to use list comprehension to do the job with random.choice:

import random

colors = {
    'purple': '#7A4198',
    'turquoise':'#9ACBC9',
    'orange': '#EF5C35',
    'blue': '#19457D',
    'green': '#5AF9B5',
    'red': ' #E04160',
    'yellow': '#F9F985'
}

color=random.choice([hex_color for color_value in colors.values()]

print(f'The new color is: {color}')

References:

2

When they ask for a random pair here they mean a key and value.

For such a dict where the key:values are country:city,

use random.choice().

Pass the dictionary keys to this function as follows:

import random
keys = list(my_dict)
country = random.choice(keys)

You may wish to track the keys that were already called in a round and when getting a fresh country, loop until the random selection is not in the list of those already "drawn"... as long as the drawn list is shorter than the keys list.

srattigan
  • 620
  • 5
  • 16
2

I just stumbled across a similar problem and designed the following solution (relevant function is pick_random_item_from_dict; other functions are just for completeness).

import random


def pick_random_key_from_dict(d: dict):
    """Grab a random key from a dictionary."""
    keys = list(d.keys())
    random_key = random.choice(keys)
    return random_key


def pick_random_item_from_dict(d: dict):
    """Grab a random item from a dictionary."""
    random_key = pick_random_key_from_dict(d)
    random_item = random_key, d[random_key]
    return random_item


def pick_random_value_from_dict(d: dict):
    """Grab a random value from a dictionary."""
    _, random_value = pick_random_item_from_dict(d)
    return random_value

# Usage
d = {...}
random_item = pick_random_item_from_dict(d)

The main difference from previous answers is in the way we handle the dictionary copy with list(d.items()). We can partially circumvent that by only making a copy of d.keys() and using the random key to pick its associated value and create our random item.

0

Try this (using random.choice from items)

import random

a={ "str" : "sda" , "number" : 123, 55 : "num"}
random.choice(list(a.items()))
#  ('str', 'sda')
random.choice(list(a.items()))[1] # getting a value
#  'num'
0

Here is a little Python code for a dictionary class that can return random keys in O(1) time. (I included MyPy types in this code for readability):

from typing import TypeVar, Generic, Dict, List
import random

K = TypeVar('K')
V = TypeVar('V')
class IndexableDict(Generic[K, V]):
    def __init__(self) -> None:
        self.keys: List[K] = []
        self.vals: List[V] = []
        self.dict: Dict[K, int] = {}

    def __getitem__(self, key: K) -> V:
        return self.vals[self.dict[key]]

    def __setitem__(self, key: K, val: V) -> None:
        if key in self.dict:
            index = self.dict[key]
            self.vals[index] = val
        else:
            self.dict[key] = len(self.keys)
            self.keys.append(key)
            self.vals.append(val)

    def __contains__(self, key: K) -> bool:
        return key in self.dict

    def __len__(self) -> int:
        return len(self.keys)

    def random_key(self) -> K:
        return self.keys[random.randrange(len(self.keys))]
Mike Gashler
  • 488
  • 5
  • 9
0

To select 50 random key values from a dictionary set dict_data:

sample = random.sample(set(dict_data.keys()), 50)
Aditya Raj
  • 21
  • 3
-1

I found this post by looking for a rather comparable solution. For picking multiple elements out of a dict, this can be used:

idx_picks = np.random.choice(len(d), num_of_picks, replace=False) #(Don't pick the same element twice)
result = dict ()
c_keys = [d.keys()] #not so efficient - unfortunately .keys() returns a non-indexable object because dicts are unordered
for i in idx_picks:
    result[c_keys[i]] = d[i]
Cadoiz
  • 975
  • 12
  • 24
-1

One way would be:

import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'OTTAWA'}
random.choice(list(d.values()))

EDIT: The question was changed a couple years after the original post, and now asks for a pair, rather than a single item. The final line should now be:

country, capital = random.choice(list(d.items()))
Frightera
  • 3,860
  • 2
  • 9
  • 23
-2
b = { 'video':0, 'music':23,"picture":12 } 
random.choice(tuple(b.items())) ('music', 23) 
random.choice(tuple(b.items())) ('music', 23) 
random.choice(tuple(b.items())) ('picture', 12) 
random.choice(tuple(b.items())) ('video', 0) 
WhatsThePoint
  • 3,097
  • 8
  • 28
  • 48
  • 3
    Welcome to Stack Overflow! Thank you for this code snippet, which might provide some limited short-term help. A proper explanation [would greatly improve](//meta.stackexchange.com/q/114762) its long-term value by showing *why* this is a good solution to the problem, and would make it more useful to future readers with other, similar questions. Please [edit] your answer to add some explanation, including the assumptions you've made. – Toby Speight Jan 18 '18 at 11:07