'inverting' a dict

I

Irmen de Jong

Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.
 
A

anton muhin

Irmen said:
Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.

It's seems like rather nice Python for me ;). Two more variants:

inverted = {}
for k, vs in d.iteritems():
inverted.update(dict([(v, k) for v in vs]))

map(
lambda (k, vs): inverted.update(dict([(v, k) for v in vs])),
d.iteritems()
)

happy new year,
anton.
 
E

engsolnom

Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.

I had a need for a 'reverse' dictionary also. This is how I approached
it:

source_dict = {'A': 1,'B': 2,'C': 3,'D': 4}
target_dict = {}
for item in source_dict.iteritems():
target_dict[item[1]] = item[0]

Or, a bit more contrived....

source_dict = {'A': [[1,2,3],100],'B': [[4,5,6],200],'C':
[[7,8,9],300]}
target_dict = {}
for item in source_dict.iteritems():
target_dict[item[1][1]] = item[0]

Disclaimer: I'm a newbie
Norm
 
Y

Yermat

or even shorter
d = {'key1' : ('value1','value2'), 'key2': ('value3',) }
dict([(v,k) for k in d.iterkeys() for v in d[k]])
{'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}


anton muhin said:
Irmen said:
Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.

It's seems like rather nice Python for me ;). Two more variants:

inverted = {}
for k, vs in d.iteritems():
inverted.update(dict([(v, k) for v in vs]))

map(
lambda (k, vs): inverted.update(dict([(v, k) for v in vs])),
d.iteritems()
)

happy new year,
anton.
 
P

Peter Otten

Irmen said:
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:
[...]

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Here's what I've come up with:

import itertools

original = {"key1": ("value1", "value2"), "key2": ("value3",)}

def forInv(original):
result = {}
for (key, values) in original.iteritems():
for val in values:
result[val] = key
return result

def updateInv(original):
result = {}
for (key, values) in original.iteritems():
result.update(dict.fromkeys(values, key))
return result

def iterInv(original):
result = {}
for (key, values) in original.iteritems():
result.update(dict(itertools.izip(values, itertools.repeat(key))))
return result

def iterInv2(original):
return dict(itertools.chain(*[itertools.izip(values,
itertools.repeat(key))
for key, values in original.iteritems()]))

def compInv(original):
return dict([(val, key) for (key, values) in original.iteritems() for
val in values])

wanted = { "value1": "key1", "value2": "key1", "value3": "key2" }

for inv in globals().values():
if callable(inv):
print inv.__name__,
if inv(original) == wanted:
print "OK"
else:
print "FAILED"

Conclusion: my favourite toys, itertools and list comprehensions, lead to
clumsier code - well, me at least. So I would recommend that you don't
listen to that voice.

Peter
 
J

Jeff Epler

Does this listcomp give the right result?
dict([(v, k) for k, vs in original.iteritems() for v in vs])

Jeff
 
W

Wade Leftwich

Irmen de Jong said:
Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.


def invert_dict(D):
return dict([(y,x) for (x,y) in D.items()])

-- Wade Leftwich
Ithaca, NY
 
I

Irmen de Jong

Jeff said:
Does this listcomp give the right result?
dict([(v, k) for k, vs in original.iteritems() for v in vs])

Yes it does, and it's even more concise than Yermat's solution

dict([(v,k) for k in d.iterkeys() for v in d[k]])

because it avoids a dict lookup every loop cycle. Thanks Jeff!

--Irmen
 
I

Irmen de Jong

Wade said:
def invert_dict(D):
return dict([(y,x) for (x,y) in D.items()])

No, this one only works for non-sequence-type values.
I wanted to map every item of a value (a sequence)
to the corresponding key.

--Irmen
 
E

Elaine Jackson

dict2=dict([(a,b) for b in dict1.keys() for a in dict1])

HTH


| Hi
| I have this dict that maps a name to a sequence of other names.
| I want to have it reversed, i.e., map the other names each to
| the key they belong to (yes, the other names are unique and
| they only occur once). Like this:
|
| { "key1": ("value1", "value2"), "key2": ("value3,) }
|
| -->
|
| { "value1": "key1", "value2": "key1", "value3": "key2" }
|
| What I'm doing is using a nested loop:
|
| dict2={}
| for (key,value) in dict1.items():
| for name in value:
| dict2[name] = key
|
| which is simple enough, but I'm hearing this little voice in
| the back of my head saying "there's a simpler solution".
| Is there? What is it? ;-)
|
| Thanks
| --Irmen.
|
 
B

Bob van der Poel

This is sort of off topic for the thread, but I've got a similar
problem. In this case I have a dict like:

{ 'key1': 'value1', 'key2': value2}

and I sometimes need to find the key for the value. All values/keys are
unique. I just use a loop:

for a in dict:
if dict[a]== targ:
return a
return None

But it'd be nice to have something faster, etc.


Elaine said:
dict2=dict([(a,b) for b in dict1.keys() for a in dict1])

HTH


| Hi
| I have this dict that maps a name to a sequence of other names.
| I want to have it reversed, i.e., map the other names each to
| the key they belong to (yes, the other names are unique and
| they only occur once). Like this:
|
| { "key1": ("value1", "value2"), "key2": ("value3,) }
|
| -->
|
| { "value1": "key1", "value2": "key1", "value3": "key2" }
|
| What I'm doing is using a nested loop:
|
| dict2={}
| for (key,value) in dict1.items():
| for name in value:
| dict2[name] = key
|
| which is simple enough, but I'm hearing this little voice in
| the back of my head saying "there's a simpler solution".
| Is there? What is it? ;-)
|
| Thanks
| --Irmen.
|
 
R

Raymond Hettinger

Irmen de Jong said:
Wade said:
def invert_dict(D):
return dict([(y,x) for (x,y) in D.items()])

No, this one only works for non-sequence-type values.
I wanted to map every item of a value (a sequence)
to the corresponding key.

--Irmen

Try a nested list comprehension:
data = { "key1": ("value1", "value2"), "key2": ("value3",) }
dict([(v,k) for k,vlist in data.iteritems() for v in vlist])
{'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}


Raymond Hettinger

P.S. In Py2.4, it will be possible to write this without the
brackets. The resulting generator expression is faster and more
memory friendly:
{'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}
 
P

Peter Abel

Irmen de Jong said:
Hi
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

{ "key1": ("value1", "value2"), "key2": ("value3,) }

-->

{ "value1": "key1", "value2": "key1", "value3": "key2" }

What I'm doing is using a nested loop:

dict2={}
for (key,value) in dict1.items():
for name in value:
dict2[name] = key

which is simple enough, but I'm hearing this little voice in
the back of my head saying "there's a simpler solution".
Is there? What is it? ;-)

Thanks
--Irmen.

.... and after all there is always a hacky one-liner:
org = {"key1": ("value1", "value2"), "key2": ("value3",)}
dict(reduce(lambda l,(k,v):l.extend(zip(v,(k,)*len(v))) or l,org.items(),[])) {'value3': 'key2', 'value2': 'key1', 'value1': 'key1'}

Happy new year
Peter
 
S

sdd

Bob said:
This is sort of off topic for the thread, but I've got a similar
problem. In this case I have a dict like:

{ 'key1': 'value1', 'key2': value2}

and I sometimes need to find the key for the value. All values/keys are
unique. I just use a loop:

for a in dict:
if dict[a]== targ:
return a
return None
For a few hunts, I'd do:
for key,value in dictionary.iteritems():
if value == target:
return key
return None

Of course, if it probed a lot between changes, it's better to

reversedictionary = dict([(v,k) for k,v in dictionary.items()])

-Scott David Daniels
(e-mail address removed)
 
N

Nick Vargish

Irmen de Jong said:
I have this dict that maps a name to a sequence of other names.
I want to have it reversed, i.e., map the other names each to
the key they belong to (yes, the other names are unique and
they only occur once). Like this:

That's almost spooky, I'm working on a project for which I needed
something like this. I've just read the entire thread and my solution
seems pretty naive:

def invertdict(d):
"""Returns a dict whose key-value pairs are the value-key pairs of d."""
ret = {}
for k in d:
ret[d[k]] = k
return ret

I only need to do it once or twice, at program start-up, so time-
efficiency wasn't a critical factor. I'm guessing the list
comprehension tactics are faster, though.

Sorry about being late to the party, I've been away from Usenet for
about three weeks...

Nick
 

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

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,586
Members
45,084
Latest member
HansGeorgi

Latest Threads

Top