Reputation: 469
I am trying to create multiple key : value pairs in a dict comprehension like this:
{'ID': (e[0]), 'post_author': (e[1]) for e in wp_users}
I am receiving "missing ','"
I have also tried it this way:
[{'ID': (e[0]), 'post_author': (e[1])} for e in wp_users]
I then receive "list indices must be integers, not str"
Which I understand, but not sure the best way in correcting this and if multiple key : value pairs is possible with dict comprehensions?
Upvotes: 39
Views: 49759
Reputation: 1388
A solution for python >= 3.9 (change the key and values as you want, although i recommend keeping the tuple unpacked even if you're going to build a new tuple)
from itertools import tee
a,b = tee(tuple_collection)
c = {k:v for k,v in a} | {v:k for k,v in b}
I'm not actually sure this is actually as efficient as a single for to place the two keys in the same dict. Depends on how good tee is and how optimized | can be i suppose. Sure looks clean though.
if you're on python 3.5+ you can use
c = dict(**{k:v for k,v in a}, **{v:k for k,v in b})
or
c = {k:v for k,v in a}
c.update({v:k for k,v in b})
instead.
Upvotes: -1
Reputation: 1121366
A dictionary comprehension can only ever produce one key-value pair per iteration. The trick then is to produce an extra loop to separate out the pairs:
{k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}
This is equivalent to:
result = {}
for e in wp_users:
for k, v in zip(('ID', 'post_author'), e):
result[k] = v
Note that this just repeats the two keys with each of your wp_users
list, so you are continually replacing the same keys with new values! You may as well just take the last entry in that case:
result = dict(zip(('ID', 'post_author'), wp_users[-1]))
You didn’t share what output you expected however.
If the idea was to have a list of dictionaries, each with two keys, then you want a list comprehension of the above expression applied to each wp_users
entry:
result = [dict(zip(('ID', 'post_author'), e)) for e in wp_users]
That produces the same output as your own, second attempt, but now you have a list of dictionaries. You’ll have to use integer indices to get to one of the dictionaries objects or use further loops.
Upvotes: 42
Reputation: 8180
I came across this old question by accident, and I'm not convinced by the accepted answer.
What is disturbing with the accepted answer? Consider this:
>>> wp_users = [(1, 'Bill'), (2, 'Bob')]
>>> {k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}
{'ID': 2, 'post_author': 'Bob'}
wp_users
, e = (1, 'Bill')
, the dict is {'ID':1, 'post_author': 'Bill'}
wp_users
, e = (2, 'Bob')
, the dict is totally overwritten to {'ID':2, 'post_author': 'Bob'}
On every iteration, all the values of the dictonary are overwritten. You can avoid a loop and jump directly to the last element of wp_users
:
>>> {k: v for e in wp_users for k, v in zip(('ID', 'post_author'), e)}
{'ID': 2, 'post_author': 'Bob'}
Or:
>>> dict(zip(('ID', 'post_author'), wp_users[-1]))
{'ID': 2, 'post_author': 'Bob'}
I think that's not what you want.
What you are trying to achieve remains unclear, but I see two options: you have a list of users (id, post_author)
and you want to create either a list of dictionaries (one dict per user) or a dictionary of tuples (one tuple per field). You can see the first version as a presentation by lines, and the second as a presentation by columns of the same data.
Try this:
>>> [dict(zip(('ID', 'post_author'), user)) for user in wp_users]
[{'ID': 1, 'post_author': 'Bill'}, {'ID': 2, 'post_author': 'Bob'}]
For each user
, zip
will create tuples ('ID', id)
and ('post_author', author)
and dict
will generate the dictionaries. Now you can access to the fields like that:
>>> ds = [dict(zip(('ID', 'post_author'), user)) for user in wp_users]
>>> ds[0]['post_author']
'Bill'
That's more unusual, but you might want one dictionary whose values are tuples:
>>> dict(zip(('ID', 'post_author'), zip(*wp_users)))
{'ID': (1, 2), 'post_author': ('Bill', 'Bob')}
zip(*wp_users)
simply creates a list of tuples [(id1, id2, ...), (post_author1, post_author2, ...)]
and the rest is similar to the previous version.
>>> d = dict(zip(('ID', 'post_author'), zip(*wp_users)))
>>> d['post_author'][0]
'Bill'
To extract a column from the line view:
>>> [d['ID'] for d in ds]
[1, 2]
To extract a line from the column view:
>>> {k:vs[1] for k, vs in d.items()}
{'ID': 2, 'post_author': 'Bob'}
Upvotes: 21
Reputation: 19264
I think your problem is that the second version is creating a list of dictionaries, not just one dictionary. You are trying to access a list with a string, which raises the error:
>>> obj = [{'data1': 67, 'data2': 78}]
>>> obj[0]['data1']
67
>>> obj['data1']
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: list indices must be integers, not str
>>>
Instead, just access the second version as `wp_list[0]['post_author'], and it should work fine:
>>> wp_users = ('Bob', 'Joe', 'Sally')
>>> wp_list = [{'ID': (e[0]), 'post_author': (e[1])} for e in wp_users]
>>> wp_list[0]['post_author']
'o'
>>> wp_list[1]['post_author']
'o'
>>> wp_list[2]['post_author']
'a'
>>>
Upvotes: 1