Создать вложенный словарь типа JSON из списка в Python - PullRequest
0 голосов
/ 06 ноября 2018

Я хочу создать вложенный словарь типа JSON из списка списков. Списки содержали полный путь к каталогу, но я разбил их на отдельные компоненты, так как думал, что это упростит создание вложенных словарей.

Пример списка:

["root", "dir1", "file.txt"] 

Ожидаемый результат:

{
    "type": "directory",
    "name": "root",
    "children": [
        {
            "type": "directory",
            "name": "dir1",
            "children": [
                {
                    "type": "file",
                    "name": "file.txt",
                }
            ]
        }
    ]
}      

Я пытался использовать рекурсивный метод, но не смог добраться до него (новичок в рекурсивных методах, и моя голова постоянно вращалась). Также попробовал итеративный метод из идеи, которую я нашел здесь (переполнение стека), которая перевернула список и построила диктовку в обратном направлении, что я вроде как заставил работать, но не смог решить одно из требований решения, которое заключается в том, что код может иметь дело с дублированием в частях путей к каталогам, поскольку оно перебирает список списков. Например, следуя последнему примеру, следующий введенный список выглядит так: -

["root", "dir1", "dir2", "file2.txt"]

и нужно создать словарь JSON, чтобы получить следующее: -

{
    "type": "directory",
    "name": "root",
    "children": [
        {
            "type": "directory",
            "name": "dir1",
            "children": [
                {
                    "type": "file",
                    "name": "file.txt",
                }
                {
                    "type": "directory",
                    "name": "dir2",
                    "children": [
                        {
                            "type": "file",
                            "name": "file2.txt"
                        }
                    ]
                }
            ]
        }
    ]
} 

и т. Д. С неизвестным количеством списков, содержащих пути к каталогам. Спасибо.

Ответы [ 3 ]

0 голосов
/ 06 ноября 2018

Вот наивное рекурсивное решение, которое просто проходит по древовидной структуре, добавляя потомков по мере необходимости, пока не будет достигнут последний элемент path (предполагается, что это файл).

import json


def path_to_json(path, root):
    if path:
        curr = path.pop(0)

        if not root:
            root["type"] = "file"
            root["name"] = curr

            if path:
                root["children"] = [{}]
                root["type"] = "directory"
                path_to_json(path, root["children"][0])
        elif path:
            try:
                i = [x["name"] for x in root["children"]].index(path[0])
                path_to_json(path, root["children"][i])
            except ValueError:        
                root["children"].append({})
                path_to_json(path, root["children"][-1])

    return root


if __name__ == "__main__":
    paths = [["root", "dir1", "file.txt"], 
             ["root", "dir1", "dir2", "file2.txt"]]
    result = {}
    print(json.dumps([path_to_json(x, result) for x in paths][0], indent=4))

Выход:

{
    "type": "directory",
    "name": "root",
    "children": [
        {
            "type": "directory",
            "name": "dir1",
            "children": [
                {
                    "type": "file",
                    "name": "file.txt"
                },
                {
                    "type": "directory",
                    "name": "dir2",
                    "children": [
                        {
                            "type": "file",
                            "name": "file2.txt"
                        }
                    ]
                }
            ]
        }
    ]
}

Попробуйте!

0 голосов
/ 06 ноября 2018

Рекурсивное решение с itertools.groupby выглядит следующим образом (при условии, что все пути являются абсолютными). Идея состоит в том, чтобы сгруппировать пути по первому элементу в списке путей. Это группирует похожие корни каталогов вместе, что позволяет нам рекурсивно вызывать функцию в этой группе.

Также обратите внимание, что имена файлов не могут дублироваться в каталоге, поэтому все файлы будут сгруппированы как списки из одного элемента по groupby:

from itertools import groupby
from operator import itemgetter

def build_dict(paths):
    if len(paths) == 1 and len(paths[0]) == 1:
        return {"type": "file", "name": paths[0][0]}
    dirname = paths[0][0]
    d = {"type": "directory", "name": dirname, "children": []}
    for k, g in groupby(sorted([p[1:] for p in paths], key=itemgetter(0)),
                        key=itemgetter(0)):
        d["children"].append(build_dict(list(g)))
    return d    

paths = [["root", "dir1", "file.txt"], ["root", "dir1", "dir2", "file2.txt"]]
print(build_dict(paths))

выход

{
  "type": "directory",
  "name": "root",
  "children": [
    {
      "type": "directory",
      "name": "dir1",
      "children": [
        {
          "type": "directory",
          "name": "dir2",
          "children": [
            {
              "type": "file",
              "name": "file2.txt"
            }
          ]
        },
        {
          "type": "file",
          "name": "file.txt"
        }
      ]
    }
  ]
}
0 голосов
/ 06 ноября 2018

Учитывая, что не было предоставлено много подробностей, вот решение, которое использует ссылку для ввода каждого вложенного dict

In [537]: structure = ["root", "dir1", "dir2", "file2.txt"]

In [538]: d = {}

# Create a reference to the current dict
In [541]: curr = d

In [542]: for i, s in enumerate(structure):
     ...:     curr['name'] = s
     ...:     if i != len(structure) - 1:
     ...:         curr['type'] = 'directory'
     ...:         curr['children'] = {}
     ...:         curr = curr['children']          # New reference is the child dict
     ...:     else:
     ...:         curr['type'] = 'file'
     ...:

In [544]: from pprint import pprint

In [545]: pprint(d)
{'children': {'children': {'children': {'name': 'file2.txt', 'type': 'file'},
                           'name': 'dir2',
                           'type': 'directory'},
              'name': 'dir1',
              'type': 'directory'},
 'name': 'root',
 'type': 'directory'}

Я не знаю, будет ли это работать для всех ваших вопросов, так как спецификация не очень подробная

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...