简体   繁体   English

在 Python 中转换字典的嵌套列表

[英]Converting a nested list of dictionary in Python

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谢谢

Solution解决方案

You can use collections.defaultdict and dict.setdefault :您可以使用collections.defaultdictdict.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.想法: dict是可变的。
  • working_dict is reference table for all "id" s. working_dict是所有"id"的参考表。
  • If there is no such id, register {} for it.如果没有这样的 id,请为其注册{}
  • And register elements without id field, as root elements into result_dict .并将没有id字段的元素作为根元素注册到result_dict

Append附加

If you don't want to use collections.defaultdict , you can only use dict.setdefault .如果您不想使用collections.defaultdict ,则只能使用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)

You can also do it manually with a recursive function.您也可以使用递归函数手动执行此操作。 The idea:想法:

  • Iterate over the element in the input list迭代输入列表中的元素
  • Ignore element without id key忽略没有id键的元素
  • For element with an id in keys:对于键中带有id元素:
    • recursively search for this key in the out在 out 中递归搜索此键
    • add the element (if element already existing, the element is add in the recursive function, else after).添加元素(如果元素已经存在,则在递归函数中添加元素,否则在之后)。
# Recursive search
def iterdictAdd(d, id, name):
    # For all element in the dict
    for k, v in d.items():
        # If key match -> add
        if k == id:
            d[id] = {**d[id], **{name: {}}}
            return True
        else:
            # Recursive call
            if isinstance(v, dict):
                if iterdictAdd(v, id, name):
                    return True
    return False


out = {}
# Iterate all dict
for dict_ in nested_list:
    # Ignore elements without "id" 
    if "id" in dict_:
        # Search and add this key
        if not iterdictAdd(out, dict_["id"], dict_["name"]):
            out[dict_["id"]] = {dict_["name"]: {}}

print(out)
# {'fruit': {
#     'apple': {},
#     'grape': {},
#     'pineapple': {}
# },
#  'vehicle': {
#     'car': {
#         'sedan': {}
#         }
#     }
# }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM