Reputation: 1540
I'm working on a python AWS Cognito implementation using boto3. jwt.decode
on the IdToken yields a payload that's in the form of a dictionary, like so:
{
"sub": "a uuid",
"email_verified": True,
"iss": "https://cognito-idp....",
"phone_number_verified": False,
"cognito:username": "19407ea0-a79d-11e6-9ce4-09487ca06884",
"given_name": "Aron Filbert",
"aud": "my client app",
"token_use": "id",
"auth_time": 1480547504,
"nickname": "Aron Filbert",
"phone_number": "+14025555555",
"exp": 1480551104,
"iat": 1480547504,
"email": "[email protected]"
}
So I designed a User class that consumes that dictionary. Works great, until I need to hit Cognito again and grab fresh user details to make sure nothing changed (say, from another device). My return payload from the get_user()
call ends up looking like a list of dictionaries:
[
{
"Name": "sub",
"Value": "a uuid"
},
{
"Name": "email_verified",
"Value": "true"
},
{
"Name": "phone_number_verified",
"Value": "false"
},
{
"Name": "phone_number",
"Value": "+114025555555"
},
{
"Name": "given_name",
"Value": "Aron Filbert"
},
{
"Name": "email",
"Value": "[email protected]"
}
]
Since I might be hitting that get_user()
Cognito endpoint a lot, I'm looking for an efficient way to grab JUST the values of each dictionary in the list and use them to form the keys:values of a new dictionary. Example:
{
"sub": "a uuid", # From first list item
"email_verified": True, # From next list item
...
}
Being new to Python, I'm struggling with how to accomplish this elegantly and efficiently.
Upvotes: 2
Views: 181
Reputation: 162
A dictionary comprehension, as Andras answered above, is a simple, Pythonic one-liner for your case. Some style guidelines (such as Google's), however, recommend against them if they introduce complex logic or take up more than two or three lines:
Okay to use for simple cases. Each portion must fit on one line: mapping expression, for clause, filter expression. Multiple for clauses or filter expressions are not permitted. Use loops instead when things get more complicated.
Yes:
result = [] for x in range(10): for y in range(5): if x * y > 10: result.append((x, y)) for x in xrange(5): for y in xrange(5): if x != y: for z in xrange(5): if y != z: yield (x, y, z) return ((x, complicated_transform(x)) for x in long_generator_function(parameter) if x is not None) squares = [x * x for x in range(10)] eat(jelly_bean for jelly_bean in jelly_beans if jelly_bean.color == 'black')
No:
result = [(x, y) for x in range(10) for y in range(5) if x * y > 10] return ((x, y, z) for x in xrange(5) for y in xrange(5) if x != y for z in xrange(5) if y != z)
Dictionary comprehension is perfectly appropriate in your instance, but for the sake of completeness, this is a general method for performing operations with a couple of for-loops if you decide to do anything fancier:
for <dict> in <list>:
for <key>, <value> in <dict>:
# Perform any applicable operations.
<new_dict>[<key>] = <value>
which comes out to...
user = get_user()
user_info = {}
for info in user:
for name, value in info:
n, v = info[name], info[value]
if v.lowercase() == 'true':
v = True
else if v.lowercase() == 'false':
v = False
user_info[n] = v
Upvotes: 1
Reputation: 35080
As I noted in a comment, the bulk of your work can be done by a dict comprehension:
lst = get_user() # or something similar, lst is a list of dicts
parsed_res = {k["Name"]:k["Value"] for k in lst}
This only differs from your expected output in that it contains 'true'
and 'false'
whereas you want bools in your final result. Well, the simplest solution is to define a function that does this conversion for you:
def boolify(inp):
if inp=='true':
return True
elif inp=='false':
return False
else:
return inp
parsed_res = {k["Name"]:boolify(k["Value"]) for k in lst}
The same thing could be done in the comprehension itself, but it wouldn't be any clearer, nor efficient. This way you can do additional manipulations in your keys if you later realize that there are other stuff you want to do with your payload before storing.
Upvotes: 2