Let's say I have a regular "dict-of-dicts" as follows:
d = {}
d['a'] = {}
d['a']['b'] = 3
I can of course access the element using d['a']['b'].
In my case, I have a recursive application, in which I keep the current state as a list of keys. So I would have
my_key = ['a', 'b']
How do I access the value 3, using my_key? The issue, of course, is that my_key can be arbitrarily long (deep).
I realize I can write another traversal function or so, but it seems like there should be a straightforward way of doing so. Any ideas?
You could use reduce to iteratively index each layer of dict with a different key:
>>> from functools import reduce #only necessary in 3.X
>>> d = {}
>>> d['a'] = {} #I'm assuming this is what you meant to type
>>> d['a']['b'] = 3
>>> keys = ("a", "b")
>>> reduce(dict.get, keys, d)
3
Currently dictionary keys can only be hashable types, list (ListType) is not one of them, so if you try to specify a list as a dictionary key:
{}[[]]
you'll get:
TypeError: unhashable type: 'list'`.
You could enhance the current dictionary, allowing to specify a list as a key, and iterate over the list on inner objects. Here's the code (note that it only takes care of the get/read part):
from types import DictType, ListType
class EnhancedDictType(DictType):
def __getitem__(self, key):
if key and isinstance(key, ListType):
new_obj = self
for item in key:
new_obj = new_obj[item]
return new_obj
else:
return super(EnhancedDictType, self).__getitem__(key)
dict = EnhancedDictType
Here's also some test code:
d = dict()
d[1] = dict()
d[1][2] = dict({3: 4})
d[(1, 2, 3)] = 5
print d
print d[1]
print d[1][2]
print d[[1, 2]]
print d[[1 ,2, 3]]
print d[(1, 2, 3)]
Related
I'm trying to build a method where if an item is not in a dictionary then it uses the last member of a list and updates the dictionary accordingly. Sort of like a combination of the pop and setdefault method. What I tried was the following:
dict1 = {1:2,3:4,5:6}
b = 7
c = [8,9,10]
e = dict1.setdefault(b, {}).update(pop(c))
So I would like the output to be where {7:10} gets updated to dict1, that is to say, if b is not in the keys of dict1 then the code updates dict1 with an item using b and the last item of c.
It might be possible for you to abuse a defaultdict:
from collections import defaultdict
c = [8, 9, 10]
dict1 = defaultdict(c.pop, {1: 2, 3: 4, 5: 6})
b = 7
e = dict1[b]
This will pop an item from c and make it a value of dict1 whenever a key missing from dict1 is accessed. (That means the expression dict1[b] on its own has side-effects.) There are many situations where that behaviour is more confusing than helpful, though, in which case you can opt for explicitness:
if b in dict1:
e = dict1[b]
else:
e = dict1[b] = c.pop()
which can of course be wrapped up in a function:
def get_or_pop(mapping, key, source):
if key in mapping:
v = mapping[key]
else:
v = mapping[key] = source.pop()
return v
⋮
e = get_or_pop(dict1, b, c)
Considering your variables, you could use the following code snippet
dict1[b] = dict1.pop(b, c.pop())
where you are updating the dictionary "dict1" with the key "b" and the value c.pop(), (last value of the list in c, equivalent to c[-1] in this case). Note that this is possible because the key value b=7 is not in you original dictionary.
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?
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()))
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.
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
>>> 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).
Try this:
import random
a = dict(....) # a is some dictionary
random_key = random.sample(a, 1)[0]
This definitely works.
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()))
Since the original post wanted the pair:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'}
country, capital = random.choice(list(d.items()))
(python 3 style)
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'
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.
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().
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
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:
*Python 3.8: Standard Library Documentation - Built-in types: Dictionary view objects
Python 3.8: Data Structures - List Comprehensions:
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.
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'
To select 50 random key values from a dictionary set dict_data:
sample = random.sample(set(dict_data.keys()), 50)
I needed to iterate through ranges of keys in a dict without sorting it each time and found the Sorted Containers library. I discovered that this library enables random access to dictionary items by index which solves this problem intuitively and without iterating through the entire dict each time:
>>> import sortedcontainers
>>> import random
>>> d = sortedcontainers.SortedDict({1: 'a', 2: 'b', 3: 'c'})
>>> random.choice(d.items())
(1, 'a')
>>> random.sample(d.keys(), k=2)
[1, 3]
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]
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))]
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)
I have made a small demo of a more complex problem
def f(a):
return tuple([x for x in range(a)])
d = {}
[d['1'],d['2']] = f(2)
print d
# {'1': 0, '2': 1}
# Works
Now suppose the keys are programmatically generated
How do i achieve the same thing for this case?
n = 10
l = [x for x in range(n)]
[d[x] for x in l] = f(n)
print d
# SyntaxError: can't assign to list comprehension
You can't, it's a syntactical feature of the assignment statement. If you do something dynamic, it'll use different syntax, and thus not work.
If you have some function results f() and a list of keys keys, you can use zip to create an iterable of keys and results, and loop over them:
d = {}
for key, value in zip(keys, f()):
d[key] = value
That is easily rewritten as a dict comprehension:
d = {key: value for key, value in zip(keys, f())}
Or, in this specific case as mentioned by #JonClements, even as
d = dict(zip(keys, f()))
I have list which have keys of dictionary. How to access the dictionary using these keys dynamically. e.g
key_store = ['test','test1']
mydict = {"test":{'test1':"value"},"test3":"value"}
So how to access mydict using key_store I want to access mydict['test']['test1'].
Note: key_store store depth of keyword means it have keywords only its value will be dictionary like test have dictionary so it have 'test','test1'
You can do this with a simple for-loop.
def get_nested_key(keypath, nested_dict):
d = nested_dict
for key in keypath:
d = d[keypath]
return d
>>> get_nested_key(('test', 'test1'), Dict)
Add error checking as required.
Use recursion:
def get_value(d, k, i):
if not isinstance(d[k[i]], dict):
return d[k[i]]
return get_value(d[k[i]], k, i+1)
The parameters are the dictionary, the list and an index you'll be running on.
The stop condition is simple; Once the value is not a dictionary, you want to return it, otherwise you continue to travel on the dictionary with the next element in the list.
>>> key_store = ['test','test1']
>>> Dict = {"test":{'test1':"value"},"test3":"value"}
>>> def get_value(d, k, i):
... if isinstance(d[k[i]], str):
... return d[k[i]]
... return get_value(d[k[i]], k, i+1)
...
>>> get_value(Dict, key_store, 0)
'value'
You could do this with a simple dictionary reduce:
>>> mydict = {"test": {'test1': "value"}, "test3": "value"}
>>> print reduce(dict.get, ['test', 'test1'], mydict)
value
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?
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()))
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.
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
>>> 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).
Try this:
import random
a = dict(....) # a is some dictionary
random_key = random.sample(a, 1)[0]
This definitely works.
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()))
Since the original post wanted the pair:
import random
d = {'VENEZUELA':'CARACAS', 'CANADA':'TORONTO'}
country, capital = random.choice(list(d.items()))
(python 3 style)
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'
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.
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().
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
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:
*Python 3.8: Standard Library Documentation - Built-in types: Dictionary view objects
Python 3.8: Data Structures - List Comprehensions:
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.
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'
To select 50 random key values from a dictionary set dict_data:
sample = random.sample(set(dict_data.keys()), 50)
I needed to iterate through ranges of keys in a dict without sorting it each time and found the Sorted Containers library. I discovered that this library enables random access to dictionary items by index which solves this problem intuitively and without iterating through the entire dict each time:
>>> import sortedcontainers
>>> import random
>>> d = sortedcontainers.SortedDict({1: 'a', 2: 'b', 3: 'c'})
>>> random.choice(d.items())
(1, 'a')
>>> random.sample(d.keys(), k=2)
[1, 3]
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]
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))]
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)