Рекурсивное преобразование dict

#python #recursion

#python #рекурсия

Вопрос:

Пример dict :

 simple_dict = {
    'root 1': [
        'root 1 1',
        'root 1 2'
    ],
    'root 2': [
        'root 2 1',
        'root 2 2',
        {'root 2 3': [
            'root 2 3 1',
            'root 2 3 2'
        ]},
        'root 2 4',
    ],
    'root 3': ['root 3 1']
}
  

Я пытаюсь преобразовать его в это :

 root_dict = [
    {
        'name': 'root1',
        'children': [
            {
                'name': 'root 1 1'
            },
            {
                'name': 'root 1 2'
            }
        ]
    },
    {
        'name': 'root 2',
        'children': [
            {
                'name': 'root 2 1'
            },
            {
                'name': 'root 2 2'
            },
            {
                'name': 'root 2 3',
                'children': [
                    {
                        'name': 'root 2 3 1'
                    },
                    {
                        'name': 'root 2 3 2'
                    }

                ]
            },
            {
                'name': 'root 2 4'
            }
        ]
    },
    {
        'name': 'root 3',
        'children': [
            {
                'name': 'root 3 1'
            }
        ]
    }
]
  

все, что я получил, это написал функцию, которая рекурсивно пересекает словарь, но теряет третий уровень вложенности:

 def dict_to_tree_options(tree):
    options = []
    for key, value in tree.items():
        elem = {
            'name': f'{key}',
            'children': []
        }
        #print(key, value)
        for i in value:
            if isinstance(i, dict):
                #print('deeper')
                outer_elem = {
                    'name': f'{i}'
                }
                dict_to_tree_options(i)
            else:
                inner_elem = {
                    'name': f'{i}'
                }
                elem['children'].append(inner_elem)
                #print(key, i, 'n')    
        options.append(elem)
    print(options)
  
 [{'name': 'root 2 3', 'children': [{'name': 'root 2 3 1'}, {'name': 'root 2 3 2'}]}]
[{'name': 'root 1', 'children': [{'name': 'root 1 1'}, {'name': 'root 1 2'}]}, {'name': 'root 2', 'children': [{'name': 'root 2 1'}, {'name': 'root 2 2'}, {'name': 'root 2 4'}]}, {'name': 'root 3', 'children': [{'name': 'root 3 1'}]}]
  

по-видимому, я не понимаю, как должна работать рекурсивная функция, потому что мне кажется, что каждый раз, когда вызывается рекурсия, переменная options переопределяется, и я не понимаю, что с ней происходит дальше

Комментарии:

1. Вторая структура выглядит еще большей мерзостью, чем первая. Какова здесь цель? И да, поскольку ваша первая строка — options = [] это то, что она будет воссоздаваться при каждом вызове. Вы хотели передать это?

2. Мне нужно преобразовать первый словарь в формат второго, потому что система, которую я использую, принимает его только в этой форме. Посмотрите на вторую строку вывода, это почти то, что мне нужно, но это освобождает 3-й рычаг root, пожалуйста, помогите мне, мне это нужно

Ответ №1:

Вот реализация, которая выполняет то, что вы просили:

 def transform(value):
    if not isinstance(value, (dict, list)):
        return {'name': value}

    if isinstance(value, list):
        return [transform(item) for item in value]

    return [{'name': key, 'children': transform(value_)} for key, value_ in value.items()]
  

Комментарии:

1. Почти работает, но есть небольшая проблема: [{'name': 'root 2 3', 'children': [{'name': 'root 2 3 1'}, {'name': 'root 2 3 2'}]}] эта часть заключена в список, но не должна быть

2. Код приводит к желаемому результату, который вы хотели, вы не можете сохранить его только как dict, поскольку name in не уникален….

Ответ №2:

Вот способ сделать это. При выполнении рекурсивной функции всегда сначала думайте о своих условиях остановки!

 def format_dict(entry):
    if not isinstance(entry, (list, dict)):
        return {'name': entry}
    
    if isinstance(entry, list):
        return [format_dict(e) for e in entry]
    
    if isinstance(entry, dict):
        children = [{
            'name': key, 'children': format_dict(value) 
        } for key, value in entry.items()]
        
        if len(children) == 1:
            return children[0]
        
        return children
  

Комментарии:

1. Спасибо за прекрасное рабочее решение, но я получил None , если значение содержит целое число, можете ли вы это исправить, пожалуйста?

2. Готово, просто замените if isinstance(entry, str) на if not isinstance(entry, (list, dict))

3. Да, это так, я только что добавил return {'name': str(entry)} , большое вам спасибо! :3

Ответ №3:

Вы могли бы попытаться вернуть массив параметров. Затем, когда элемент является dict, вы должны добавить {name: f'{I}’, дочерние элементы: dict_to_tree_options(I)} к дочерним элементам.

Ответ №4:

Вы можете использовать рекурсию с пониманием списка:

 def to_tree(d):
   if not isinstance(d, dict):
      return {'name':d}
   return [{'name':a, 'children':list(map(to_tree, b))} for a, b in d.items()]
  

 import json
simple_dict = {'root 1': ['root 1 1', 'root 1 2'], 'root 2': ['root 2 1', 'root 2 2', {'root 2 3': ['root 2 3 1', 'root 2 3 2']}, 'root 2 4'], 'root 3': ['root 3 1']} 
print(json.dumps(to_tree(simple_dict), indent=4))
  

Вывод:

 [
   {
      "name": "root 1",
      "children": [
          {
             "name": "root 1 1"
          },
          {
             "name": "root 1 2"
          }
       ]
   },
   {
      "name": "root 2",
      "children": [
         {
            "name": "root 2 1"
         },
         {
            "name": "root 2 2"
         },
         [
             {
                 "name": "root 2 3",
                 "children": [
                     {
                        "name": "root 2 3 1"
                     },
                     { 
                        "name": "root 2 3 2"
                      }
                  ]
              }
          ],
          {
              "name": "root 2 4"
          }
      ]
  },
  {
     "name": "root 3",
     "children": [
          {
            "name": "root 3 1"
          }
       ]
    }
]