So basically I'm traversing through the nested dictionary extension like so:
extension['value1']['value2']['value3']['value4']
However, sometimes the dict file can be a little different:
extension['value1']['value2']['blah1']['value4']
How can I account for this scenario? I don't have to worry about a large number of scenarios, the key will only ever be value3 or blah1
You could write a function to get the first key that exists:
def get_first_item(items, keys):
for k in keys:
if k in items:
return items[k]
raise KeyError
And then use it like this:
get_first_item(extension['value1']['value2'], ['value3', 'blah1'])['value4']
You have to explicitly check for the key and then fetch its value. For example:
optional_keys = ['value3', 'blah1']
value = None
for optional_key in optional_keys:
if optional_key in extension['value1']['value2']:
value = extension['value1']['value2'][optional_key]['value4']
break
I think the above two answers can well fix your problem. Since your key will be either value3 or blah1, instead of a function, you may as well use the following code when you loop through the dictionary:
try:
value = extension['value1']['value2']['value3']['value4']
except Exception as e: # except KeyError:
# print(e)
value = extension['value1']['value2']['blah1']['value4']
Related
I have a script where I read a file line by line and save some information to lists and dictionary. Namely, I store the keys (say ley1 and key2) to pass to the dictionary and a list to be stored as item in the dictionary.
It happens that I have to update a dictionary only if some conditions are met, i.e.:
myDict = {}
if mylist:
myDict[key1][key2] = mylist
Of course this will raise a KeyError if key2 does not exist already. Therefore, I introduced the following function:
def updateDict2keys(myDict,mykey1,mykey2,myitems):
"""
updates a dictionary by appending values at given keys (generating key2 if not given)
input: key1, key2 and items to append
output: dictionary orgnanized as {mykey1:{mykey2:myitems}}
"""
try:
myDict[mykey1][mykey2] = myitems
except KeyError:
myDict[mykey1] = {mykey2:myitems}
# output
return myDict
My question is: is it "safe" to call such a function in the main code inside a for loop like this?
with open(os.path.join(path+myfile)) as ntwkf:
# read file
rdlistcsv = csv.reader(ntwkf)
rowslist = [line for line in rdlistcsv]
ntwkJuncDict = {}
for idx,f in enumerate(rowslist): # loop over file lines
if f:
lineelmnt = f[0].split()[0]
else:
continue
# flags
isBranchName = True if lineelmnt=='definitions' else False
isJunction = True if lineelmnt=='connections' else False
# update dictionary
if isBranchName:
reachname = f[0].split()[2].replace("'","")
if isJunction:
usreach = f[0].split()[2].replace("'","")
uschain = float(f[1].replace("'","").replace(" ",""))
if usreach:
uslist = [usreach, uschain]
todivide.append(uslist)
ntwkJuncDict = updateDict2keys(ntwkJuncDict, reachname, 'upstream', uslist)
I must say, my code works pretty well, I am just asking myself (and yourselves of course!) if I am doing everything the python way and if there are smarter solutions.
Instead of accessing the primary key, use dict.setdefault with a default empty dict on it.
def updateDict2keys(myDict,mykey1,mykey2,myitems):
myDict.setdefault(mykey1, {})[mykey2] = myitems
return myDict
Note that your initial approach is also safe. Only lookup, not assignment, throws KeyError. In the statement myDict[mykey1][mykey2] = myitems, a KeyError can only be thrown of mykey1 is not set. Thus, setting it to an empty dict on a KeyError does not overwrite anything.
I have a dictionary which has subkeys and sub values. I want to print a list of the main keys that contain a certain value in the subkey.
I hope the example makes it clear:
cssStyleDict= {'.c13':{'color':'#000',
'font-family':'"Arial"',
'font-weight':'700',
'vertical-align':'baseline'},
'.c6':{'font-weight':'700'},
'.c7':{'color':'#000',
'font-size':'11pt',
'font-weight':'700',
'text-decoration':'none'},
'.c2':{'background-color':'#ff0'}}
I want to print a list of all keys that contain {'font-weight':'700'}.
I have tried this:
def getKeysByValue(dictOfElements, valueToFind):
listOfKeys = list()
listOfItems = dictOfElements.items()
for item in listOfItems:
if item[1] == valueToFind:
listOfKeys.append(item[0])
return listOfKeys
listOfKeys = getKeysByValue(cssStyleDict, {'font-weight':'700'} )
for key in listOfKeys:
print(key)
But of course this only gives me an exact match. I've also tried using a regex expression, but to no avail.
The output should be a list containing .c13 .c6 .c7
Thanks in advance if anyone can help.
You can try this way:
>>> [ i for i in cssStyleDict if cssStyleDict[i].get('font-weight') == '700' ]
['.c13', '.c6', '.c7']
To use the code that you’ve posted as a base for your answer, you only need to change the condition of the if-statement. Currently, you are checking for a perfect match because you have used “==“
However, Python comes with a great operator that does exactly what you need: “in”.
Thus, your new condition should be:
If valueToFind in item[1]:
Hope this makes sense!
Clean and Clear Approach using dictonary built-ins.
cssStyleDict= {'.c13':{'color':'#000',
'font-family':'"Arial"',
'font-weight':'700',
'vertical-align':'baseline'},
'.c6':{'font-weight':'700'},
'.c7':{'color':'#000',
'font-size':'11pt',
'font-weight':'700',
'text-decoration':'none'},
'.c2':{'background-color':'#ff0'}}
for key, val in cssStyleDict.items():
if(cssStyleDict[key].__contains__('font-weight')):
if(cssStyleDict[key]['font-weight'] == '700'):
print(key)
I would do it by first checking each dict for that key, then comparing the value to the expected one.
def getKeysByValue(dictOfElements, valueToFind):
listOfKeys = list()
listOfItems = dictOfElements.items()
for k,v in listOfItems:
try:
if v[valueToFind[0]] == valueToFind[1]:
listOfKeys.append(k)
except KeyError:
pass
return listOfKeys
listOfKeys = getKeysByValue(cssStyleDict, ('font-weight', '700') )
for key in listOfKeys:
print(key)
I'm trying to parse a JSON response I get from a web API. The problem is that the JSON can have varying levels, which translate into dictionaries of dictionaries, with an occasional list in the mix.
Example (this works):
for r in json_doc['results']:
yield r.get('lastLoginLocation',{}).get('coordinates',{}).get('lat',{})
Can I do the same thing when a list of dictionaries is in there? I'd like it to return the specified key value from the first dictionary in the list if the list is populated, or return '{}' if the list is empty.
Example (this does NOT work)
yield r.get('profile',{}).get('phones',{})[0].get('default',{})
Quite simply in `get('phones') use a list with one empty dict as default, ie:
yield r.get('profile',{}).get('phones',[{}])[0].get('default',{})
Note that this will still break with an IndexError if r["profile"]["phones"] is an empty list. You could get around using a or, ie:
yield (r.get('profile',{}).get('phones',[{}]) or [{}])[0].get('default',{})
but it's getting really messy (and builds two empty dicts and lists for no good reason), so you'd probably be better with more explicit code, cf Pankaj Singhal's answer.
Your approach is quite suboptimal, since a missing profile key in the root dictionary does not terminate the search, but continues further on unnecessarily. There obviously would not be any keys in an empty dict.
You could instead use a try/except:
def get_value(container, keys=None)
if keys is None:
raise ValueError
for r in container:
item = r
for i in keys:
try:
item = item[i]
except (IndexError, KeyError):
yield {}
break
# finished cleanly
else:
yield item
get_value(json_doc['results'], keys=['profile', 'phones', 0, 'default'])
This get_nested helper function might be what you want. I used a similar technique in some XML parsing code in the past. It removes the
implementation from obscuring what your code is actually trying to achieve.
from contextlib import suppress
def get_nested(list_or_dict, keys, default={}):
"""Get value from nested list_or_dict using keys. If the current
level is a dict, lookup the current key. If the current
level is a list, lookup current key in the first element of the
list. Return default for any errors.
"""
def get(item, key):
if hasattr(item, 'get') and key in item:
return item[key]
raise KeyError
for key in keys:
with suppress(KeyError):
list_or_dict = get(list_or_dict, key)
continue
with suppress(IndexError, KeyError):
list_or_dict = get(list_or_dict[0], key)
continue
break
else:
return list_or_dict
return default
Your code to call it would be like this:
for r in json_doc['results']:
yield get_nested(r, ('lastLoginLocation', 'coordinates', 'lat'))
yield get_nested(r, ('profile', 'phones', 'default'))
I'm using Python 3.5.1
So what I am trying to do is pass in a dict a dot separated string representing the path to a key and a default value. I want to check for the keys existence and if it's not there , provide the default value. The problem with this is that the key I want to access could be nested in other dicts and I will not know until run time. So what I want to do is something like this:
def replace_key(the_dict, dict_key, default_value):
if dict_key not in the_dict:
the_dict[dict_key] = default_value
return the_dict
some_dict = {'top_property': {'first_nested': {'second_nested': 'the value'}}}
key_to_replace = 'top_property.first_nested.second_nested'
default_value = 'replaced'
#this would return as {'top_property': {'first_nested': {'second_nested': 'replaced'}}}
replace_key(some_dict, key_to_replace, default_value)
What I'm looking for is a way to do this without having to do a split on '.' in the string and iterating over the possible keys as this could get messy. I would rather not have to use a third party library. I feel like there is clean built in Pythonic way to do this but I just can't find it. I've dug through the docs but to no avail. If anyone has any suggestion as to how I could do this it would be very much appreciated. Thanks!
You could use recursivity:
def replace_key(the_dict, dict_keys, default_value):
if dict_keys[0] in the_dict:
if len(dict_keys)==1:
the_dict[dict_keys[0]]=default_value
else:
replace_key(the_dict[dict_keys[0]], dict_keys[1:],default_value)
else:
raise Exception("wrong key")
some_dict = {'top_property': {'first_nested': {'second_nested': 'the value'}}}
key_to_replace = 'top_property.first_nested.second_nested'
default_value = 'replaced'
#this would return as {'top_property': {'first_nested': {'second_nested': 'replaced'}}}
replace_key(some_dict, key_to_replace.split("."), default_value)
But it still uses the split(). But maybe you consider it to be less messy?
the easyest way that I've found to do this, namely get value using a "key path" by "dotted string" is using replace and eval:
for key in pfields:
if key.find('.') > 0:
key = key.replace(".", "']['")
try:
data = str(eval(f"row['{key}']"))
except KeyError:
data = ''
And this is an example of the keys:
lfields = ['cpeid','metadata.LinkAccount','metadata.DeviceType','metadata.SoftwareVersion','mode_props.vfo.CR07.VIKPresence','mode_props.vfo.CR13.VIBHardVersion']
With this raw solution You don't need install other library
I often deal with heterogeneous datasets and I acquire them as dictionaries in my python routines. I usually face the problem that the key of the next entry I am going to add to the dictionary already exists.
I was wondering if there exists a more "pythonic" way to do the following task: check whether the key exists and create/update the corresponding pair key-item of my dictionary
myDict = dict()
for line in myDatasetFile:
if int(line[-1]) in myDict.keys():
myDict[int(line[-1])].append([line[2],float(line[3])])
else:
myDict[int(line[-1])] = [[line[2],float(line[3])]]
Use a defaultdict.
from collections import defaultdict
d = defaultdict(list)
# Every time you try to access the value of a key that isn't in the dict yet,
# d will call list with no arguments (producing an empty list),
# store the result as the new value, and give you that.
for line in myDatasetFile:
d[int(line[-1])].append([line[2],float(line[3])])
Also, never use thing in d.keys(). In Python 2, that will create a list of keys and iterate through it one item at a time to find the key instead of using a hash-based lookup. In Python 3, it's not quite as horrible, but it's still redundant and still slower than the right way, which is thing in d.
Its what that dict.setdefault is for.
setdefault(key[, default])
If key is in the dictionary, return its value. If not, insert key with a value of default and return default. default defaults to None.
example :
>>> d={}
>>> d.setdefault('a',[]).append([1,2])
>>> d
{'a': [[1, 2]]}
Python follows the idea that it's easier to ask for forgiveness than permission.
so the true Pythonic way would be:
try:
myDict[int(line[-1])].append([line[2],float(line[3])])
except KeyError:
myDict[int(line[-1])] = [[line[2],float(line[3])]]
for reference:
https://docs.python.org/2/glossary.html#term-eafp
https://stackoverflow.com/questions/6092992/why-is-it-easier-to-ask-forgiveness-than-permission-in-python-but-not-in-java
Try to catch the Exception when you get a KeyError
myDict = dict()
for line in myDatasetFile:
try:
myDict[int(line[-1])].append([line[2],float(line[3])])
except KeyError:
myDict[int(line[-1])] = [[line[2],float(line[3])]]
Or use:
myDict = dict()
for line in myDatasetFile:
myDict.setdefault(int(line[-1]),[]).append([line[2],float(line[3])])