2

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

Ali
  • 79
  • 9
  • 1
    `dic.keys()[0]` or the like? – Thomas Weller Jun 21 '16 at 10:58
  • Nope, the correct form is `list(dic.keys())[0]` which is good because get rid of the loop but still creates a list which for my purpose seems unnecessary. – Ali Jun 21 '16 at 12:19
  • "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?": it's only a lot of overhead if you are adding new keys and have to make a new list of key very often. if the dictionary remains the same most of the time, the only overhead is getting the random key from the key list: `list(dic.keys())`. – Rick Jun 21 '16 at 14:20

5 Answers5

3

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 ordered by insertion order. 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 insertion order. 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.
GrantJ
  • 8,162
  • 3
  • 52
  • 46
2

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
hashcode55
  • 5,622
  • 4
  • 27
  • 40
  • `random.choice` still has to iterate through the dictionary in the background. – Rick Jun 21 '16 at 11:21
  • Your instructions looks great. But as you mentioned still there's a need to make a list (in Python 3 which I use) and as Rick pointed out there's still implicit iteration. By the way thank you. – Ali Jun 21 '16 at 12:18
  • 1
    I think the time complexity won't differ much, as I said in the comment... – hashcode55 Jun 21 '16 at 12:26
1

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.

Rick
  • 43,029
  • 15
  • 76
  • 119
  • But the work done to iterate through the list in`random.choice` won't be nearly same as to keeping the dictionary in the sorted fashion? Moreover I don't think `random.choice` would be using brute force. (taking O(n) to iterate over the list). – hashcode55 Jun 21 '16 at 11:29
  • @hashcode55 Now that you ask the question I find I'm uncertain about the internal workings of `SortedDict`. However the first paragraph of the documentation says it "efficiently maintains its keys in sorted order", which to me would mean it's probably O(1) or O(log n). At any rate, I don't know much about the internals of `random.choice` either, but I don't see how it is possible for it to get a random key without iterating. – Rick Jun 21 '16 at 12:33
  • @hashcode55 however, i agree- with your comment on the other answer: i doubt any of this matters much. – Rick Jun 21 '16 at 12:38
  • 1
    Yes it says maintaining which I can agree but to first sort the keys it has to use any efficient algorithm which can go minimum as O(nlogn). Maintaining can be done by binary search which is again O(logn). – hashcode55 Jun 21 '16 at 13:09
  • 1
    @hashcode55 Yeah I am not all that familiar with calculating big O notation but what you're saying feels right. The first sort issue seems like it may or may not be important. Depends on how often the rand key is needed in comparison to how often a sorted dictionary is created. But it's worth mentioning in the answer. – Rick Jun 21 '16 at 14:07
0

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()]
smassey
  • 5,875
  • 24
  • 37
-4

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]

ankush madaan
  • 300
  • 1
  • 16
  • You aren't answering the question. Where is the solution to choosing an arbitrary key? – Paul Rooney Jun 21 '16 at 11:04
  • check the updated code, i thought you would be able to get it from there. – ankush madaan Jun 21 '16 at 11:08
  • if you find it correct please mark it correct because i didn't understand why it get down-rating although its correct. Thanks – ankush madaan Jun 21 '16 at 13:03
  • I honestly didn't get why the solution presented in this post is irrelavent to my question and received negative reputations since I just wanna choose a key, any key (if dictionary's not empty) not even necessary to be randomly chosen, without having to create a list or going through iteration (which turned out to be no need for it by simply using `list(dic.keys())[0]`). Frankly speaking this answer seems to me as fine as others' because here there's also one extra list (much shorter, only 1 long). But I guess 4th line need a slightly revision:`randomKey=random.sample(yourdict.keys(),1)` – Ali Jun 21 '16 at 13:18
  • @Ali it doesn't answer the question because `random.sample` still iterates through the dictionary. it's impossible to do this with a `dict` object without iterating through the keys. – Rick Jun 21 '16 at 14:17