[dictionary] how to get key by item

E

Egor Bolonev

saluton al ciuj

i know how to get item by key

==================
dict = {10 : 50, 2 : 12, 4 : 43}

print dict[2]

but i wonder how to get key by item

print dict[12]
==================


is there a more fast way than that one (my dictionary is really big)
==================
dict = {10 : 50, 2 : 12, 4 : 43}
item = 12
for key in dict.keys():
if dict[key] == item:
print key
break
==================
 
S

Skip Montanaro

Egor> i know how to get item by key
...
Egor> but i wonder how to get key by item

Assuming your dictionary defines a one-to-one mapping, just invert it:
>>> forward = {10 : 50, 2 : 12, 4 : 43}
>>> reverse = dict([(v,k) for (k,v) in forward.iteritems()])
>>> print forward {10: 50, 4: 43, 2: 12}
>>> print reverse
{50: 10, 43: 4, 12: 2}

That doubles your storage, so you'll have to trade that off against the
speed gain of not having to loop over the entire dictionary.

Skip
 
R

Roy Smith

Skip Montanaro said:
Egor> i know how to get item by key
...
Egor> but i wonder how to get key by item

Assuming your dictionary defines a one-to-one mapping, just invert it:
forward = {10 : 50, 2 : 12, 4 : 43}
reverse = dict([(v,k) for (k,v) in forward.iteritems()])
print forward {10: 50, 4: 43, 2: 12}
print reverse
{50: 10, 43: 4, 12: 2}

That doubles your storage, so you'll have to trade that off against the
speed gain of not having to loop over the entire dictionary.

Well, you *do* loop over the entire dictionary, but you only do it once,
when you create the reverse dict. If you are only going to do a single
lookup, it's no gain, but if you amortize the cost over many lookups,
it's almost certainly a big win.

This raises an interesting question. Let's assume that you add all the
entries to the dictionary before you do any lookups, and you then need
to lookup things up in both directions. Which is faster, to
simultaneously build both the forward and reverse dicts, or to just
build the forward one and when you're done doing that, build the reverse
one in a single shot with the above list comprehension?

BTW, does Python really build the intermediate list and throw it away
after using it to initialize the dictionary, or is it smart enough to
know that it doesn't really need to build the whole list in memory?
 
S

Skip Montanaro

Roy> Well, you *do* loop over the entire dictionary, but you only do it
Roy> once, when you create the reverse dict. If you are only going to
Roy> do a single lookup, it's no gain, but if you amortize the cost over
Roy> many lookups, it's almost certainly a big win.

Sure, but the OP said his dictionary was big. It's up to him to decide
whether the space-time tradeoff is worth it (or even possible).

Roy> BTW, does Python really build the intermediate list and throw it
Roy> away after using it to initialize the dictionary, or is it smart
Roy> enough to know that it doesn't really need to build the whole list
Roy> in memory?

That's why I called .iteritems() in my example. It won't generate the
entire list of tuples as .items() would.

Skip
 
R

Roy Smith

Skip Montanaro said:
Roy> BTW, does Python really build the intermediate list and throw it
Roy> away after using it to initialize the dictionary, or is it smart
Roy> enough to know that it doesn't really need to build the whole list
Roy> in memory?

That's why I called .iteritems() in my example. It won't generate the
entire list of tuples as .items() would.

I know it won't generate the list of items from the forward dict, but I
was thinking of the list generated by the list comprehension, passed as
the argument to the reverse dict constructor. That's the throw-away
list I was thinking of (see Tim Delaney's response to my post).
 
A

Aahz

Assuming your dictionary defines a one-to-one mapping, just invert it:
forward = {10 : 50, 2 : 12, 4 : 43}
reverse = dict([(v,k) for (k,v) in forward.iteritems()])
print forward {10: 50, 4: 43, 2: 12}
print reverse
{50: 10, 43: 4, 12: 2}

That doubles your storage, so you'll have to trade that off against the
speed gain of not having to loop over the entire dictionary.

To be precise, it doubles the storage of the *dictionary*, but it does
*NOT* double the storage of the keys and items. Depending on how big
those are, the cost of building a second dict might be mostly lost in the
noise.
 
K

Keith Dart

Skip said:
Egor> i know how to get item by key
...
Egor> but i wonder how to get key by item

Assuming your dictionary defines a one-to-one mapping, just invert it:
forward = {10 : 50, 2 : 12, 4 : 43}
reverse = dict([(v,k) for (k,v) in forward.iteritems()])
print forward {10: 50, 4: 43, 2: 12}
print reverse
{50: 10, 43: 4, 12: 2}

That doubles your storage, so you'll have to trade that off against the
speed gain of not having to loop over the entire dictionary.

Skip

But beware that all the items in the original dictionary must be
hashable. The example shows just integers, so I assume they are in this
case. But generally, this may not work.



--
\/ \/
(O O)
-- --------------------oOOo~(_)~oOOo----------------------------------------
Keith Dart <[email protected]>
vcard: <http://www.kdart.com/~kdart/kdart.vcf>
public key: ID: F3D288E4 URL: <http://www.kdart.com/~kdart/public.key>
============================================================================
 
F

Fredrik Lundh

Skip said:
That doubles your storage

careful: it creates another dictionary structure with the same size as the first
one, but it doesn't copy the objects in the dictionary.

so whether it doubles the actual memory usage depends on what data you
have in the dictionary (last time I checked, ints and dictionary slots were the
same size, but I cannot think of any other object that isn't larger...)

(but you knew that, of course)

</F>
 
O

Ola Natvig

Skip said:
Egor> i know how to get item by key
...
Egor> but i wonder how to get key by item

Assuming your dictionary defines a one-to-one mapping, just invert it:
forward = {10 : 50, 2 : 12, 4 : 43}
reverse = dict([(v,k) for (k,v) in forward.iteritems()])
print forward {10: 50, 4: 43, 2: 12}
print reverse
{50: 10, 43: 4, 12: 2}

That doubles your storage, so you'll have to trade that off against the
speed gain of not having to loop over the entire dictionary.

Skip

If some keys has the same value as the item this will cause problems
because keys in your result dictionary can be overwritten. Could it be a
option to build the result dictionary as a dictionary with the values
as the keys, and lists of keys as the value. Perhaps you need to use a
loop for this.
 
N

Nick Coghlan

Ola said:
If some keys has the same value as the item this will cause problems
because keys in your result dictionary can be overwritten. Could it be a
option to build the result dictionary as a dictionary with the values
as the keys, and lists of keys as the value. Perhaps you need to use a
loop for this.

<<Python 2.4>>

..>>> d = dict(foo=1, bar=1, bob=7, jane=42, mary=16, fred=16)
..>>> from itertools import groupby
..>>> val = d.__getitem__
..>>> grouped = groupby(sorted(d.iterkeys(), key=val), val)
..>>> r = dict((value, list(keys)) for value, keys in grouped)
..>>> r
{16: ['mary', 'fred'], 1: ['bar', 'foo'], 42: ['jane'], 7: ['bob']}

Cheers,
Nick.
 
S

Skip Montanaro

Fredrik> careful: it creates another dictionary structure with the same
Fredrik> size as the first one, but it doesn't copy the objects in the
Fredrik> dictionary.

Yes, sorry. The OP indicated the original dictionary was very big, so it
seemed like duplicating the dictionary storage would potentially be costly
since dictionaries hold extra storage (on average, twice the storage needed
to hold the references to its keys?) to support O(1) average time lookup.

Skip
 
S

Skip Montanaro

Ola> If some keys has the same value as the item this will cause
Ola> problems because keys in your result dictionary can be
Ola> overwritten.

That's why I said, "assuming your dictionary defines a one-to-one
mapping...".

Skip
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,982
Messages
2,570,189
Members
46,734
Latest member
manin

Latest Threads

Top