Yet another nested dictionary comprehension in Python

Viewed 109

I have not been able to understand how to comprehend my problem in the terms presented in other nested python dictionary examples, so am posting a new question in the hope of specific answers!

Question 1: I want to understand if its possible to use the traditional "for key, item in dict.items()" dictionary comprehension to get at the values in my dictionary.

testdict = dict(
[('PERSON 1',
  {'bonus': 600000,
   'deferral_payments': 'NaN',
   'email_address': 'person.1@something.com',
   'total_stock_value': 585062}),
 ('PERSON 2',
  {'bonus': 'NaN',
   'deferral_payments': 1295738,
   'email_address': 'NaN',
   'total_stock_value': 10623258})
   ])

# This works in the desired way, it unpacks the individual items of the nested dictionary rows...

for key in testdict.keys():
    for item in testdict[key]:
        print("{0}, {1}, {2}".format(key, item, testdict[key][item]))

#PERSON 1, bonus, 600000
#PERSON 1, deferral_payments, NaN
#PERSON 1, email_address, person.1@something.com
#PERSON 1, total_stock_value, 585062
#PERSON 2, bonus, NaN
#PERSON 2, deferral_payments, 1295738
#PERSON 2, email_address, NaN
#PERSON 2, total_stock_value, 10623258

# Trying the traditional approach using key, item in dict.items()

for key, item in testdict.items():
    print("{0}, {1}".format(key, item))

    # If I try print(testdict[key][item]) here I get the error "TypeError: unhashable type: 'dict'"

#PERSON 1, {'bonus': 600000, 'deferral_payments': 'NaN', 'email_address': 'person.1@something.com', 'total_stock_value': 585062}
#PERSON 2, {'bonus': 'NaN', 'deferral_payments': 1295738, 'email_address': 'NaN', 'total_stock_value': 10623258}

With the traditional approach using key, item in dict.items() I cannot get the same result - how do I use this approach correctly?


Question 2: Is there any way of including an if clause higher up in my code example (or in any solution to my request on question 1)? There are some keys in my nested dictionary that should have numeric values but currently have NaN, I can get to these in the following way, but is there any way to include the conditional clause as part of the loop?

value_keys = ["bonus", 
              "deferral_payments", 
              "total_stock_value"]

for key in testdict.keys():
    for item in testdict[key]:
        if item in value_keys and testdict[key][item] == "NaN":
            print("{0}, {1}, {2}".format(key, item, testdict[key][item]))

#PERSON 1, deferral_payments, NaN
#PERSON 2, bonus, NaN
3 Answers

For your first question, no matter what you do, you have to iterate the outer dictionary and then each inner dictionary. Hopefully this helps:

>>> for outer_key, outer_value in testdict.items():
...     for inner_key, inner_value in outer_value.items():
...         print("{0}, {1}, {2}".format(outer_key, inner_key, inner_value))
...
PERSON 1, bonus, 600000
PERSON 1, deferral_payments, NaN
PERSON 1, email_address, person.1@something.com
PERSON 1, total_stock_value, 585062
PERSON 2, bonus, NaN
PERSON 2, deferral_payments, 1295738
PERSON 2, email_address, NaN
PERSON 2, total_stock_value, 10623258

I don't understand your second question, but you should ask it as a separate question anyway. (A "question" on Stack Overflow that's actually two unrelated questions is not very useful for future visitors.)

item is also a dictionary. So you can just use another for loop.

for k1, item in testdict.items():
    for k2, v in item.items():
        print("{0}, {1}, {2}".format(k1, k2, v))

You can add the condition like this:

for k1, item in testdict.items():
    for k2, v in item.items():
        if v == 'NaN':
            print("{0}, {1}, {2}".format(k1, k2, v))

You can try this:

testdict = dict(
[('PERSON 1',
  {'bonus': 600000,
   'deferral_payments': 'NaN',
   'email_address': 'person.1@something.com',
   'total_stock_value': 585062}),
 ('PERSON 2',
  {'bonus': 'NaN',
    'deferral_payments': 1295738,
    'email_address': 'NaN',
    'total_stock_value': 10623258})
  ])

new_data = [(a, ', '.join(map(' '.join, [(c, str(d) if isinstance(d, int) else d) for c, d in b.items()]))) for a, b in testdict.items()]

for a, b in new_data:
    print(a, b)

Output:

('PERSON 2', 'bonus NaN, email_address NaN, deferral_payments 1295738, total_stock_value 10623258')
('PERSON 1', 'bonus 600000, email_address person.1@something.com, deferral_payments NaN, total_stock_value 585062')
Related