How to create dynamic dictionary and add list content to it

Viewed 55

I have created the below logic to add list to a dictionary in Python it works fine

>>> dict ={}
>>> dict ={"key":[]}
>>> dict['key']
[]
>>> dict['key'].append('a')
>>> dict['key']
['a']

When I try to Implement same thing while parsing the JSON and creating dynamic dictionary based on certain it failed to add in the List and report as NoneType

import json
json_data="""{
   "data":[
      {
         "package_path":"/bin/bash",
         "package_type":"rpm"
      },
      {
         "package_path":"com.test3",
         "package_type":"java"
      },
      {
         "package_path":"com.test",
         "package_type":"java"
      }
   ]
}
"""

j_dict = json.loads(json_data)

dict2 = {}
for vuln in j_dict['data']:
   package_type = vuln['package_type']
   package_path = vuln['package_path']

   if package_type in dict2:
      dict2 ={package_type:[]}
   else:
      dict2[package_type].append(package_path)

Throws error

 % python ~/Desktop/test.py
Traceback (most recent call last):
  File "test.py", line 30, in <module>
    dict2[package_type].append(package_path)
KeyError: u'rpm'

Expecting output like

dict2 {"java":['com.test3','com.test2'],"rpm":['/bin/bash']}

3 Answers

You can use dict.setdefault to create empty list if the key is not found inside the dictionary. For example:

import json
json_data="""{
   "data":[
      {
         "package_path":"/bin/bash",
         "package_type":"rpm"
      },
      {
         "package_path":"com.test3",
         "package_type":"java"
      },
      {
         "package_path":"com.test",
         "package_type":"java"
      }
   ]
}
"""

j_dict = json.loads(json_data)
out = {}
for vuln in j_dict['data']:
    out.setdefault(vuln['package_type'], []).append(vuln['package_path'])

print(out)

Prints:

{'rpm': ['/bin/bash'], 'java': ['com.test3', 'com.test']}

defaultdict can be used here.

import json
from collections import defaultdict

json_data = """{
   "data":[
      {
         "package_path":"/bin/bash",
         "package_type":"rpm"
      },
      {
         "package_path":"com.test3",
         "package_type":"java"
      },
      {
         "package_path":"com.test",
         "package_type":"java"
      }
   ]
}
"""

j_dict = json.loads(json_data)
data = defaultdict(list)
for entry in j_dict['data']:
    data[entry['package_type']].append(entry['package_path'])
print(data)

output

defaultdict(<class 'list'>, {'rpm': ['/bin/bash'], 'java': ['com.test3', 'com.test']})

You have your if statement slightly wrong:

if package_type in dict2:
   dict2 ={package_type:[]}
else:
   dict2[package_type].append(package_path)

Should be:

if package_type not in dict2:
   dict2[package_type] = []

dict2[package_type].append(package_path)

You were saying, if a key was in the dictionary, replace the whole dictionary with a single key and a list as the value.

In the else clause you were saying, if the key doesn't exist, fetch the value for that key, assume it's a list, and append to it. Which fails.

This idiom of either adding value if it doesn't exist or using the existing value is so common, there are a couple of different ways of doing it built into python and its libraries.

dict.setdefault will either set a new default value for a key, or return the existing value if it exists. I find its call syntax ugly:

dict2.setdefault(package_type, []).append(package_path)

This sets the key's value to [] if it doesn't exist, returns it, and then you can append to the list as it exists in the dictionary.

An alternative that I prefer is to use collections.defaultdict, which is a dictionary that automatically creates a default value when a key doesn't already exist:

dict2 = defaultdict(list)

dict2[package_type].append(package_path)
Related