dict.keys()[0] on Python 3 [duplicate] - python

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)

Related

Order of dictionary keys when performing conditional list comprehension in Python

In Python 3.10, I am aware that a dictionary preserves insertion order. However when performing conditional list comprehensions, can this order still be guaranteed?
For example, given:
my_dict = {}
my_dict['a'] = 1
my_dict['b'] = 2
my_dict['c'] = 3
my_dict['d'] = 4
Can one guarantee that either (option A):
print([k for k in my_dict.keys() if k not in ['c']])
or (option B):
print([k for k in (my_dict.keys() - {'c'})])
will always return:
['a', 'b', 'd']
Iterating over dict or dict.keys() should give the same results for any version of Python, since the language guarantees the current order will always be stable, even if it doesn't necessarily match the insertion order. In Python 3, the keys() method provides a dynamic view of the dictionary's entries, so it will directly reflect the current state of the dict. The views themselves may be "set-like", but that does not imply they are unordered (or independently ordered).
The problem with the examples in the question is that they don't compare like with like. The keys() method returns a view (or a list in earlier versions), whereas keys() - {'a'} evaluates to a set (i.e. an object with no guaranteed order). So it is safe to assume option A will always give the same results, but not option B.
I think the short answer is yes, the "preserves insertion order" clause gives you a proper order of keys whenever you go through them (be it via for k in my_dict or my_dict.keys()), and together with the one that #Larry pointed out gives you what you ask for.
However the downwotes on this question are probably due to the fact that if you need an answer to this question for a coding problem, you should either learn more about list comprehensions or just rethink your solution and sort the keys based on insertion order or whatever way of guaranteing you'd imagine

Python 3.5 OrderedDict: applying sorted to a nested dictionary iterator

So the python documentation suggests using itemgetter, attrgetter, or methodgetter from the operator module when applying sorted on complex data types. Further, iterators are smaller and faster than lists for large size objects.
Thus I am wondering how to create an iterator on an OrderDict's values. The reason being that in the OrderDict I wish to sort all the values are also (regular) dictionaries.
For regular dictionaries, one could do this with:
sorted(my_dict.itervalues(), key=itemgetter('my_key'))
however OrderedDict only seems to have the method __iter__() which works on the OrderedDict keys.
So how can I efficiently make an iterator for the values of the OrderedDict.
Note, I am not looking for list comprehension, a lambda function, or extracting the relevant sub key (key inside the dictionary (a value)) values of the OrderedDict.
e.g.
sorted (my_dict, key= lambda key: my_dict[key]['my_key'])
example nested:
test = OrderedDict({'a': {'x':1, 'y':2, 'z':3},
'b': {'x':1, 'y':2, 'z':3}
})
Neither dict nor OrderedDict have an itervalues() method in Python 3. That method only exists in Python 2.
Use dict.values():
sorted(my_dict.values(), key=itemgetter('my_key'))
In Python 2 you want to use itervalues() not so much because it is an iterator, but because dict.values() had to create a new list object which is then discarded again. Iterables are also not faster (rather, they are often slower!), they are instead more memory efficient. In this case it is faster because not having to create a (large) list that you then discard again takes time.
In Python 3, dict.values() creates a view instead, a lightweight object that like dict.itervalues() yields values on demand and doesn't have to produce a list up front.
You don't have to call iter() on this. sorted() takes an iterable, and will itself call iter() on whatever you passed in. Because it does this from native code and doesn't have to look up a global name, it can do this much faster than Python code ever could.
The answer is to call the method .values() to get a view and type set it to iter:
sorted(iter(my_dict.values()), key=itemgetter('my_subkey'))

Choose one key arbitrarily in a dictionary without iteration [duplicate]

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]

Get: TypeError: 'dict_values' object does not support indexing when using python 3.2.3 [duplicate]

This question already has answers here:
TypeError: 'dict_keys' object does not support indexing
(5 answers)
Closed 6 years ago.
This is my code:
{names[i]:d.values()[i] for i in range(len(names))}
This works completely fine when using python 2.7.3; however, when I use python 3.2.3, I get an error stating 'dict_values' object does not support indexing. How can I modify the code to make it compatible for 3.2.3?
In Python 3, dict.values() (along with dict.keys() and dict.items()) returns a view, rather than a list. See the documentation here. You therefore need to wrap your call to dict.values() in a call to list like so:
v = list(d.values())
{names[i]:v[i] for i in range(len(names))}
A simpler version of your code would be:
dict(zip(names, d.values()))
If you want to keep the same structure, you can change it to:
vlst = list(d.values())
{names[i]: vlst[i] for i in range(len(names))}
(You can just as easily put list(d.values()) inside the comprehension instead of vlst; it's just wasteful to do so since it would be re-generating the list every time).
In Python 3 the dict.values() method returns a dictionary view object, not a list like it does in Python 2. Dictionary views have a length, can be iterated, and support membership testing, but don't support indexing.
To make your code work in both versions, you could use either of these:
{names[i]:value for i,value in enumerate(d.values())}
    or
values = list(d.values())
{name:values[i] for i,name in enumerate(names)}
By far the simplest, fastest way to do the same thing in either version would be:
dict(zip(names, d.values()))
since zip() doesn't care one way or the other.
Note however, that all of these methods will give you results that will vary depending on the actual contents of d. To overcome that, you may be able use an OrderedDict✶ instead, which remembers the order that keys were first inserted into it, so you can count on the order of what is returned by the values() method.
✶ Update in Python 3.7+ regular dictionaries maintain insertion order, so using an OrderedDict would no longer be necessary. (Actually they were also that way in CPython 3.6, but it wasn't official yet — i.e. in the sense that it was merely an implementation detail, not part of the language specification).

How does this code sort a dictionary?

I was looking for ways to sort a dictionary and came across this code on a SO thread:
import operator
x = {1: 2, 3: 4, 4:3, 2:1, 0:0}
sorted_x = sorted(x.iteritems(), key=operator.itemgetter(1))
How does this code work?
When I call iteritems() over a dictionary I get this:
<dictionary-itemiterator object at 0xf09f18>
I know that this is a reference, but how do you use it?
And afaik, in sorted(a,b), as is supposed to be the thing you want to sort, and b would be the indicator for sorting right? How does itemgetter(1) work here?
operator.itemgetter(1) is equivalent to lambda x: x[1]. It's an efficient way to specify a function that returns the value at index 1 of its input.
.iteritems() is a method of a dictionary that returns an iterator over the entries in the dictionary in (key,value) tuple form.
iteritems() is just like items(), except it returns an iterator rather than a list. For large dictionaries, this saves memory because you can iterate over each individual element without having to build up the complete list of items in memory first.
sorted accepts a keyword argument key which is a function used to determine what to compare by when sorting something. In this case, it is using operator.itemgetter, which is like the function version of doing something[1]. Therefore, the code is sorting on the [1] item of the tuples returned by items(), which is the value stored in the dictionary.
Most python built-ins which deal with lists or list like objects also accept iterators, these are like a pointer into the list, which you can advance to the next item in the list with the next() member function. This can be very convenient for infinite lists or very large lists, (either many elements or very large elements,) to keep memory usage down. See http://docs.python.org/library/stdtypes.html#iterator-types
iteritems() gives an iterator into the list of items in the dictionary.

Categories

Resources