Converting a nested list of dictionary in Python

Yehui He :

I would like to convert a nested list of dictionary into a substructure. And finding a robust way to do so. The structure:

nested_list = [
  {
     "id" : "fruit",
     "name" : "apple"
  },
  {
     "name": "fruit"
  },
  {
     "id" : "fruit",
     "name" : "grape"
  },
  {
     "id" : "fruit",
     "name" : "pineapple"
  },
  {
     "name": "vehicle"
  },
  {
    "id" : "vehicle",
     "name": "car"
  },
  {
    "id" : "car",
     "name": "sedan"
  },
] 

Into:

{
  "vehicle": {
    "car": {
        "sedan" : {}
     }
  },
  "fruit" : {
     "apple": {},
    "grape": {},
    "pineapple": {}
  }
}

Note that in this case it can go two level down. But It can go three deep down as well. For example one additional entry:

{ 
   "id" : "sedan", 
   "name": "mini sedan" 
}

My approach so far is:

for category in nested_list:
    if 'id' not in category:
        d[category['name']] = {}

for category in nested_list:
    if 'id' in category and category['id'] in d:
        d[category['id']][category['name']] = {}
    elif 'id' in category and category['id'] not in d:
        for k, v in d.items():
            if category['id'] in v:
                d[k][category['id']] = {category['name']: {}}
    # If there are not top level key then do nothing
    else:
        pass

It works in this case. The problem is it's not robust enough. I'm thinking recursion but unable to crack it. Can someone help out? Thank you

Boseong Choi :

Solution

You can use collections.defaultdict and dict.setdefault:

from collections import defaultdict

nested_list = [
    {
        "id": "fruit",
        "name": "apple"
    },
    {
        "name": "fruit"
    },
    {
        "id": "fruit",
        "name": "grape"
    },
    {
        "id": "fruit",
        "name": "pineapple"
    },
    {
        "name": "vehicle"
    },
    {
        "id": "vehicle",
        "name": "car"
    },
    {
        "id": "car",
        "name": "sedan"
    },
    {
        "id": "sedan",
        "name": "mini sedan"
    },
]

working_dict = defaultdict(dict)
result_dict = {}

for item in nested_list:
    name = item['name']
    if 'id' in item:
        id_ = item['id']
        working_dict[id_].setdefault(name, working_dict[name])
    else:
        result_dict[name] = working_dict[name]
print(working_dict)
print(result_dict)

output:

defaultdict(<class 'dict'>, {'fruit': {'apple': {}, 'grape': {}, 'pineapple': {}}, 'apple': {}, 'grape': {}, 'pineapple': {}, 'vehicle': {'car': {'sedan': {'mini sedan': {}}}}, 'car': {'sedan': {'mini sedan': {}}}, 'sedan': {'mini sedan': {}}, 'mini sedan': {}})
{'fruit': {'apple': {}, 'grape': {}, 'pineapple': {}}, 'vehicle': {'car': {'sedan': {'mini sedan': {}}}}}

Explanation

  • The idea: dict is mutable.
  • working_dict is reference table for all "id"s.
  • If there is no such id, register {} for it.
  • And register elements without id field, as root elements into result_dict.

Append

If you don't want to use collections.defaultdict, you can only use dict.setdefault. But it is more verbose.

working_dict = {}
result_dict = {}

for item in nested_list:
    name = item['name']
    if 'id' in item:
        id_ = item['id']
        working_dict.setdefault(id_, {}).setdefault(name, working_dict.setdefault(name, {}))
    else:
        result_dict[name] = working_dict.setdefault(name, {})
print(result_dict)

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=386279&siteId=1