This question already has answers here:
dict.keys()[0] on Python 3 [duplicate]
(3 answers)
Closed 6 years ago.
I just wanna make sure that in Python dictionaries there's no way to get just a key (with no specific quality or relation to a certain value) but doing iteration. As much as I found out you have to make a list of them by going through the whole dictionary in a loop. Something like this:
list_keys=[k for k in dic.keys()]
The thing is I just need an arbitrary key if the dictionary is not empty and don't care about the rest. I guess iterating over a long dictionary in addition to creation of a long list for just randomly getting a key is a whole lot overhead, isn't it?
Is there a better trick somebody can point out?
Thanks
A lot of the answers here produce a random key but the original question asked for an arbitrary key. There's quite a difference between those two. Randomness has a handful of mathematical/statistical guarantees.
Python dictionaries are not ordered in any meaningful way. So, yes, accessing an arbitrary key requires iteration. But for a single arbitrary key, we do not need to iterate the entire dictionary. The built-in functions next and iter are useful here:
key = next(iter(mapping))
The iter built-in creates an iterator over the keys in the mapping. The iteration order will be arbitrary. The next built-in returns the first item from the iterator. Iterating the whole mapping is not necessary for an arbitrary key.
If you're going to end up deleting the key from the mapping, you may instead use dict.popitem. Here's the docstring:
D.popitem() -> (k, v), remove and return some (key, value) pair as a 2-tuple;
but raise KeyError if D is empty.
You can use random.choice
rand_key = random.choice(dict.keys())
And this will only work in python 2.x, in python 3.x dict.keys returns an iterator, so you'll have to do cast it into a list -
rand_key = random.choice(list(dict.keys()))
So, for example -
import random
d = {'rand1':'hey there', 'rand2':'you love python, I know!', 'rand3' : 'python has a method for everything!'}
random.choice(list(d.keys()))
Output -
rand1
You are correct: there is not a way to get a random key from an ordinary dict without using iteration. Even solutions like random.choice must iterate through the dictionary in the background.
However you could use a sorted dict:
from sortedcontainers import SortedDict as sd
d = sd(dic)
i = random.randrange(len(d))
ran_key = d.iloc[i]
More here:.
http://www.grantjenks.com/docs/sortedcontainers/sorteddict.html
Note that whether or not using something like SortedDict will result in any efficiency gains is going to be entirely dependent upon the actual implementation. If you are creating a lot of SD objects, or adding new keys very often (which have to be sorted), and are only getting a random key occasionally in relation to those other two tasks, you are unlikely to see much of a performance gain.
How about something like this:
import random
arbitrary_key = random.choice( dic.keys() )
BTW, your use of a list comprehension there really makes no sense:
dic.keys() == [k for k in dic.keys()]
check the length of dictionary like this, this should do !!
import random
if len(yourdict) > 0:
randomKey = random.sample(yourdict,1)
print randomKey[0]
else:
do something
randomKey will return a list, as we have passed 1 so it will return list with 1 key and then get the key by using randomKey[0]
Related
Good afternoon.
I'm sorry if my question may seem dumb or if it has already been posted (I looked for it but didn't seem to find anything. If I'm wrong, please let me know: I'm new here and I may not be the best at searching for the correct questions).
I was wondering if it was possible to remove (pop) a generic item from a dictionary in python.
The idea came from the following exercise:
Write a function to find the sum of the VALUES in a given dictionary.
Obviously there are many ways to do it: summing dictionary.values(), creating a variable for the sum and iterate through the dict and updating it, etc.. But I was trying to solve it with recursion, with something like:
def total_sum(dictionary):
if dictionary == {}:
return 0
return dictionary.pop() + total_sum(dictionary)
The problem with this idea is that we don't know a priori which could be the "first" key of a dict since it's unordered: if it was a list, the index 0 would have been used and it all would have worked.
Since I don't care about the order in which the items are popped, it would be enough to have a way to delete any of the items (a "generic" item). Do you think something like this is possible or should I necessarily make use of some auxiliary variable, losing the whole point of the use of recursion, whose advantage would be a very concise and simple code?
I actually found the following solution, which though, as you can see, makes the code more complex and harder to read: I reckon it could still be interesting and useful if there was some built-in, simple and direct solution to that particular problem of removing the "first" item of a dict, although many "artificious", alternative solutions could be found.
def total_sum(dictionary):
if dictionary == {}:
return 0
return dictionary.pop(list(dictionary.keys())[0]) + total_sum(dictionary)
I will let you here a simple example dictionary on which the function could be applied, if you want to make some simple tests.
ex_dict = {"milk":5, "eggs":2, "flour": 3}
ex_dict.popitem()
it removes the last (most recently added) element from the dictionary
(k := next(iter(d)), d.pop(k))
will remove the leftmost (first) item (if it exists) from a dict object.
And if you want to remove the right most/recent value from the dict
d.popitem()
You can pop items from a dict, but it get's destroyed in the process. If you want to find the sum of values in a dict, it's probably easiest to just use a list comprehension.
sum([v for v in ex_dict.values()])
Instead of thinking in terms of popping values, a more pythonic approach (as far is recursion is pythonic here) is to use an iterator. You can turn the dict's values into an iterator and use that for recursion. This will be memory efficient, and give you a very clean stopping condition for your recursion:
ex_dict = {"milk":5, "eggs":2, "flour": 3}
def sum_rec(it):
if isinstance(it, dict):
it = iter(it.values())
try:
v = next(it)
except StopIteration:
return 0
return v + sum_rec(it)
sum_rec(ex_dict)
# 10
This doesn't really answer the question about popping values, but that really shouldn't be an option because you can't destroy the input dict, and making a copy just to get the sum, as you noted in the comment, could be pretty expensive.
Using popitem() would be almost the same code. You would just catch a different exception and expect the tuple from the pop. (And of course understand you emptied the dict as a side effect):
ex_dict = {"milk":5, "eggs":2, "flour": 3}
def sum_rec(d):
try:
k,v = d.popitem()
except KeyError:
return 0
return v + sum_rec(d)
sum_rec(ex_dict)
# 10
We can use:
dict.pop('keyname')
I am trying to understand what is the simplest way to check if an object is in either the keys of a dictionary or in the values of a dictionary. I've tried using .items() but with no results.
Now I am using this solution but I wonder if there is a better solution:
zdict = { 'a':1,'b':2,'c':3}
print(list(zdict.values()) + list(zdict.keys()))
'b' in list(zdict.values()) + list(zdict.keys())
Stop unecessarily making list objects out of the views returned by .keys and .values. To check if an object is a dictionary key, you simply use some_object in some_dict, to check if it is in the values, you use some_object in some_dict.values(), so combining both:
some_object in some_dict or some_obect in some_dict.values()
This is fundamentally going to be a linear operation altogether, but checking if it is in the keys is constant-time, it's a hash-lookup, so you should check that first to take advantage of short-circuiting behavior. Note, if you make a list out of the keys then you force a linear search.
I wouldn't say this is simpler but maybe:
any('b'==k or 'b'==v for k,v in zdict.items())
"b" in sum(zdict.items(), ())
We turn the "tuples of list" into a tuple of all keys and values together, making use of the sum where we supply an empty tuple for its initial value.
Edit: A commenter said above is a quadratic operation. A linear version might be:
from itertools import chain
"b" in chain(*zdict.items())
Linearity is thanks to the lazy evaluation of chain.
This is not much different than others but quite different syntax:
any([ k for k, v in zdict.items() if k=='b' or v=='b'])
This question already has answers here:
TypeError: unhashable type: 'dict', when dict used as a key for another dict [duplicate]
(2 answers)
Closed 6 years ago.
I was writing a basic uno-type card game, and wanted to be able to (while making 7 cards for the player) check if the value was already in the player's deck (I used random). I got an unhashable dict error, and some other questions were about the same error, but in different parts of code. Anyways, here's my code.
def CardGame():
nm=8
clist=["Red","Blue","Green","Yellow"]
nlist=[]
for i in range(0,10):
nlist.append(i)
pd={}
deck={"Red":0,"Red":1,"Red":2,"Red":3,"Red":4,"Red":5,"Red":6,"Red":7,"Red":8,"Red":9,"Blue":0,"Blue":1,"Blue":2,"Blue":3,"Blue":4,"Blue":5,"Blue":6,"Blue":7,"Blue":8,"Blue":9,"Green":0,"Green":1,"Green":2,"Green":3,"Green":4,"Green":5,"Green":6,"Green":7,"Green":8,"Green":9,"Yellow":0,"Yellow":1,"Yellow":2,"Yellow":3,"Yellow":4,"Yellow":5,"Yellow":6,"Yellow":7,"Yellow":8,"Yellow":9}
for i in range(1,nm):
c=random.choice(clist)
d=random.choice(nlist)
if ({c:d}) in deck:
pd.update({c:d})
del deck[c:d]
else:
nm=nm+1
print("%s %s"%(c,d))
With if ({c:d}) in deck:, you are checking if the dictionary {c:d} is present as a key in the dictionary deck. Dictionaries are unhashable (as mutable data types generally are), and a dictionary key must be hashable, so instead of just telling you "no," it throws the error you saw. A dictionary will never be present as a key in a dictionary.
Also, as noted in the comments, dictionary keys are unique, so the dictionary you made cannot exist as it appears. Consider a different data structure, like a list of tuples (e.g. [('Red', 1), ('Red', 2),...), or a dictionary with list values (e.g. {'Red':[1, 2, 3,...], 'Blue':[1, 2, 3,...],...}).
Your specific error comes from how you're trying to check for member ship in the dictionary. The in operator checks if a give key exists, but you're passing a one-element dictionary rather than just a key (you get that specific exception because dictionaries are not valid keys since they can't be hashed).
However, as Kevin commented above, you've also got a larger issue where your deck dictionary doesn't contain what you want it to contain, since you're reusing the same keys repeatedly. A dictionary can only have a single value for a given key (though the value could be a list or tuple, or some other type that contains additional items).
There are a variety of ways you can fix this issue. A set containing 2-tuples would work sort of like how you wanted your dict to work. You can add and remove elements, and efficiently check if a tuple is in the set or not with the in operator.
However, looking at what you're actually doing, I think a larger change to your algorithm will be much better. Rather than randomly picking a color and number and then checking if it is still in your deck, you should instead just select a random element from the collection directly. Rather than looping to select multiple values, use random.sample to select all the values at once. Or, if you're going to be taking more random values from the deck later, shuffle it and take a slice.
Here's a version of the code that uses random.shuffle on a list of 2-tuples, then slices off 10 of them from the end to become the pd list.
import itertools
import random
def CardGame():
nm=8
clist = ["Red","Blue","Green","Yellow"]
nlist = list(range(0,10)) # this is easier than looping to append the values
deck = list(itertools.product(clist, nlist)) # much easier than naming them all
random.shuffle(deck)
pd = deck[-10:] # slice 10 items from the end
del deck[-10:] # and then remove them from the list (fairly efficient at the end)
This question already has answers here:
Accessing dict_keys element by index in Python3
(7 answers)
Closed 2 years ago.
I have this sentence:
def Ciudad(prob):
numero = random.random()
ciudad = prob.keys()[0]
for i in prob.keys():
if(numero > prob[i]):
if(prob[i] > prob[ciudad]):
ciudad = i
else:
if(prob[i] > prob[ciudad]):
ciudad = i
return ciudad
But when I call it this error pops:
TypeError: 'dict_keys' object does not support indexing
is it a version problem? I'm using Python 3.3.2
dict.keys() is a dictionary view. Just use list() directly on the dictionary instead if you need a list of keys, item 0 will be the first key in the (arbitrary) dictionary order:
list(prob)[0]
or better still just use:
next(iter(dict))
Either method works in both Python 2 and 3 and the next() option is certainly more efficient for Python 2 than using dict.keys(). Note however that dictionaries have no set order and you will not know what key will be listed first.
It looks as if you are trying to find the maximum key instead, use max() with dict.get:
def Ciudad(prob):
return max(prob, key=prob.get)
The function result is certainly going to be the same for any given prob dictionary, as your code doesn't differ in codepaths between the random number comparison branches of the if statement.
In Python 3.x, dict.keys() does not return a list, it returns an iterable (specifically, a dictionary view). It is worth noting that dict itself is also an iterable of the keys.
If you want to obtain the first key, use next(iter(dict)) instead. (Note that before Python 3.6 dictionaries were unordered, so the 'first' element was an arbitrary one. Since 3.6 it will be based on insertion order. If you need that behaviour in older versions or with cross-version compatibility, you can use collections.OrderedDict).
This works quite simply: we take the iterable from the dictionary view with iter(), then use next() to advance it by one and get the first key.
If you need to iterate over the keys—then there is definitely no need to construct a list:
for key in dict:
...
These are all advantageous when compared to using list() as it means a list isn't constructed - making it faster and more memory efficient (hence why the default behaviour of keys() was changed in 3.x). Even in Python 2.x you would be better off doing next(iter(dict.iterkeys()).
Note all these things apply to dict.values() and dict.items() as well.
I've had success turning the iterables taken from a dictionary into a list.
So, for dic.keys(), dic.values(), and dic.items(), in Python3.6, you can:
dic = {'a':3, 'b':2, 'c':3}
print(dic)
dictkeys = dic.keys() # or values/items
print(dictkeys)
keylist = []
keylist.extend(iter(dictkeys)) # my big revelation
print('keylist', keylist)
What is the best way to generate a unique key for the contents of a dictionary. My intention is to store each dictionary in a document store along with a unique id or hash so that I don't have to load the whole dictionary from the store to check if it exists already or not. Dictionaries with the same keys and values should generate the same id or hash.
I have the following code:
import hashlib
a={'name':'Danish', 'age':107}
b={'age':107, 'name':'Danish'}
print str(a)
print hashlib.sha1(str(a)).hexdigest()
print hashlib.sha1(str(b)).hexdigest()
The last two print statements generate the same string. Is this is a good implementation? or are there any pitfalls with this approach? Is there a better way to do this?
Update
Combining suggestions from the answers below, the following might be a good implementation
import hashlib
a={'name':'Danish', 'age':107}
b={'age':107, 'name':'Danish'}
def get_id_for_dict(dict):
unique_str = ''.join(["'%s':'%s';"%(key, val) for (key, val) in sorted(dict.items())])
return hashlib.sha1(unique_str).hexdigest()
print get_id_for_dict(a)
print get_id_for_dict(b)
I prefer serializing the dict as JSON and hashing that:
import hashlib
import json
a={'name':'Danish', 'age':107}
b={'age':107, 'name':'Danish'}
# Python 2
print hashlib.sha1(json.dumps(a, sort_keys=True)).hexdigest()
print hashlib.sha1(json.dumps(b, sort_keys=True)).hexdigest()
# Python 3
print(hashlib.sha1(json.dumps(a, sort_keys=True).encode()).hexdigest())
print(hashlib.sha1(json.dumps(b, sort_keys=True).encode()).hexdigest())
Returns:
71083588011445f0e65e11c80524640668d3797d
71083588011445f0e65e11c80524640668d3797d
No - you can't rely on particular order of elements when converting dictionary to a string.
You can, however, convert it to sorted list of (key,value) tuples, convert it to a string and compute a hash like this:
a_sorted_list = [(key, a[key]) for key in sorted(a.keys())]
print hashlib.sha1( str(a_sorted_list) ).hexdigest()
It's not fool-proof, as a formating of a list converted to a string or formatting of a tuple can change in some future major python version, sort order depends on locale etc. but I think it can be good enough.
A possible option would be using a serialized representation of the list that preserves order. I am not sure whether the default list to string mechanism imposes any kind of order, but it wouldn't surprise me if it were interpreter-dependent. So, I'd basically build something akin to urlencode that sorts the keys beforehand.
Not that I believe that you method would fail, but I'd rather play with predictable things and avoid undocumented and/or unpredictable behavior. It's true that despite "unordered", dictionaries end up having an order that may even be consistent, but the point is that you shouldn't take that for granted.