Looping through and Accessing nested dictionary elements
Question:
I am trying to append dictionary elements to a list:
test1 = pd.DataFrame
list_of_origins = []
list_of_destinations = []
for test in list_of_details_per_flight:
if test['airport']['origin'] is not None:
print(test['airport']['origin']['position'])
However I get the following error for the fourth line:
TypeError: byte indices must be integers or slices, not str
When I check for the type per iteration it states it is a dictionary object, which hence should be accessible by their keys, so I think I am doing it right.
Answers:
print(type(test['airport']))
is a class dict if print(type(test['airport']['origin']))
is not a class NoneType
That’s a given, since you’d need test['airport']
to be a dictionary to be able to access ['origin']
at all – in fact, test['airport']
not being a dictionary is possibly what’s causing the error.
Have you tried printing type(test['airport'])
before the if
block? Is it always a dictionary? If it was, then if test['airport']['origin']
should not be raising that error, unless test
was ever not a dictionary, but you’ve confirmed that it is:
type(test)
is always a class dict.
print(type(test['airport']['origin']))
is a class NoneType and class dict
If that was always so, then that error would never be raised on the test['airport']['origin']['position']
. (And these two lines are the only possible sources of this error in your snippet.)
I can’t test any of these without the list_of_details_per_flight
you used, but I can suggest 3 possible ways to go about this without raising error:
Suggestion 1: add more conditions
You can individually check if that each of the 3 keys can be accessed and only print if they all can, using for...else
.
for test in list_of_details_per_flight:
for k in ['airport', 'origin', 'position']:
if not isinstance(test, dict):
# print(f"can't access ['{k}'] of {type(test)} {test}")
break
if k not in test:
# print(f"no value for ['{k}'] in {test}")
break
test = test[k] ## OR JUST
# test = test.get(k) ## [ if you want to omit the if k not in test block ]
else: print(test) ## else in for-else executes if the for block doesn't break
Suggestion 2: just print from inside a try
block
for test in list_of_details_per_flight:
try: print(test['airport']['origin']['position'])
except: pass ## OR
# except Exception as e: print(type(e), e) # print an error msg for that test
Suggestion 3: print all position
values
I have a set of functions which can be used to retrieve all values in list_of_destinations
that are paired with position
as a key.
allPos = getNestedVal(
list_of_destinations, nKey='position', rForm='_all',
pKeys=[], objName='list_of_destinations'
)
for pos in allPos: print(pos['val'], '<---', pos['expr'])
- If you want only
position
values that are inside an origin
inside an airport
, pass pKeys=['airport', 'origin']
to getNestedVal
(to specify parent keys).
pos['expr']
will contain the full keys-path like list_of_destinations[0]['airport']['origin']['position']
etc., but if you only want all the values, you can get them in a flat list by setting rForm='just_vals_all'
.
You can also combine the last two suggestions by printing all position
values in test
in the except
block.
for test in list_of_details_per_flight:
try: print(test['airport']['origin']['position'])
except Exception as e: : pass ## OR
# print(type(e), e) # print an error msg for that test
tPos = getNestedVal(test, nKey='position', rForm='_all', pKeys=[], objName='test')
# if not tPos: print('tThis test contains no position values')
for pos in tPos: print('t', pos['val'], '<---', pos['expr'])
- If you want only the first
position
value instead of a list of all of them, remove _all
from the end of rForm
.
Note: getNestedVal
can get rather slow if list_of_details_per_flight
is large; it’s mostly meant for a one-time used to find the list of keys in the path to certain values in deeply nested dictionaries, and then to use the paths from then on.
I am trying to append dictionary elements to a list:
test1 = pd.DataFrame
list_of_origins = []
list_of_destinations = []
for test in list_of_details_per_flight:
if test['airport']['origin'] is not None:
print(test['airport']['origin']['position'])
However I get the following error for the fourth line:
TypeError: byte indices must be integers or slices, not str
When I check for the type per iteration it states it is a dictionary object, which hence should be accessible by their keys, so I think I am doing it right.
print(type(test['airport']))
is a class dict ifprint(type(test['airport']['origin']))
is not a class NoneType
That’s a given, since you’d need test['airport']
to be a dictionary to be able to access ['origin']
at all – in fact, test['airport']
not being a dictionary is possibly what’s causing the error.
Have you tried printing type(test['airport'])
before the if
block? Is it always a dictionary? If it was, then if test['airport']['origin']
should not be raising that error, unless test
was ever not a dictionary, but you’ve confirmed that it is:
type(test)
is always a class dict.
print(type(test['airport']['origin']))
is a class NoneType and class dict
If that was always so, then that error would never be raised on the test['airport']['origin']['position']
. (And these two lines are the only possible sources of this error in your snippet.)
I can’t test any of these without the list_of_details_per_flight
you used, but I can suggest 3 possible ways to go about this without raising error:
Suggestion 1: add more conditions
You can individually check if that each of the 3 keys can be accessed and only print if they all can, using for...else
.
for test in list_of_details_per_flight:
for k in ['airport', 'origin', 'position']:
if not isinstance(test, dict):
# print(f"can't access ['{k}'] of {type(test)} {test}")
break
if k not in test:
# print(f"no value for ['{k}'] in {test}")
break
test = test[k] ## OR JUST
# test = test.get(k) ## [ if you want to omit the if k not in test block ]
else: print(test) ## else in for-else executes if the for block doesn't break
Suggestion 2: just print from inside a try
block
for test in list_of_details_per_flight:
try: print(test['airport']['origin']['position'])
except: pass ## OR
# except Exception as e: print(type(e), e) # print an error msg for that test
Suggestion 3: print all position
values
I have a set of functions which can be used to retrieve all values in list_of_destinations
that are paired with position
as a key.
allPos = getNestedVal(
list_of_destinations, nKey='position', rForm='_all',
pKeys=[], objName='list_of_destinations'
)
for pos in allPos: print(pos['val'], '<---', pos['expr'])
- If you want only
position
values that are inside anorigin
inside anairport
, passpKeys=['airport', 'origin']
togetNestedVal
(to specify parent keys). pos['expr']
will contain the full keys-path likelist_of_destinations[0]['airport']['origin']['position']
etc., but if you only want all the values, you can get them in a flat list by settingrForm='just_vals_all'
.
You can also combine the last two suggestions by printing all position
values in test
in the except
block.
for test in list_of_details_per_flight:
try: print(test['airport']['origin']['position'])
except Exception as e: : pass ## OR
# print(type(e), e) # print an error msg for that test
tPos = getNestedVal(test, nKey='position', rForm='_all', pKeys=[], objName='test')
# if not tPos: print('tThis test contains no position values')
for pos in tPos: print('t', pos['val'], '<---', pos['expr'])
- If you want only the first
position
value instead of a list of all of them, remove_all
from the end ofrForm
.
Note: getNestedVal
can get rather slow if list_of_details_per_flight
is large; it’s mostly meant for a one-time used to find the list of keys in the path to certain values in deeply nested dictionaries, and then to use the paths from then on.