【问题标题】:python - Efficiently parsing flattened strings into nested dictionariespython - 有效地将扁平字符串解析为嵌套字典
【发布时间】:2019-01-16 00:41:11
【问题描述】:

我正在从专有数据库中获取目录和项目列表。列表也可以是巨大的,包含数千个视图和各种嵌套。列表示例:

"MIPK",
"MIPK\/CM.toroidal",
"MIPK\/CM.Supervoid",
"MIPK\/DORAS",
"MIPK\/DORAS\/CRUDE",
"MIPK\/DORAS\/CRUDE\/CM.forest",
"MIPK\/DORAS\/CRUDE\/CM.benign",
"MIPK\/DORAS\/CRUDE\/CM.dunes",
"MIPK\/DORAS\/COMMODITIES",
"MIPK\/DORAS\/COMMODITIES\/CRUDE",
"MIPK\/DORAS\/COMMODITIES\/CRUDE\/CM.tangeant",
"MIPK\/DORAS\/COMMODITIES\/CRUDE\/CM.astral",
"MIPK\/DORAS\/COMMODITIES\/CRUDE\/CM.forking"

目录用\/大写分隔,大小写混合表示项目。

我当前返回的 JSon 是这样的:

    {
    "contents": [{
        "root_path": "MIPK",
        "root_name": "MIPK",
        "directories": [{
            "subd_name": "DORAS",
            "subd_path": "MIPK.DORAS"
        }],
        "views": [{
                "view_name": "CM.toroidal"
            },
            {
                "view_name": "CM.Supervoid"
            }
        ]
    }, {
        "root_path": "MIPK.DORAS",
        "root_name": "DORAS",
        "directories": [{
                "subd_name": "CRUDE",
                "subd_path": "MIPK.DORAS.CRUDE"
            },
            {
                "subd_name": "COMMODITIES",
                "subd_path": "MIPK.DORAS.COMMODITIES"
            }
        ],
        "views": []
    }, {
        "root_path": "MIPK.DORAS.CRUDE",
        "root_name": "CRUDE",
        "directories": [],
        "views": [{
                "view_name": "CM.forest"
            },
            {
                "view_name": "CM.benign"
            },
            {
                "view_name": "CM.dunes"
            }
        ]
    }, {
        "root_path": "MIPK.DORAS.COMMODITIES",
        "root_name": "COMMODITIES",
        "directories": [{
            "subd_name": "CRUDE",
            "subd_path": "MIPK.DORAS.COMMODITIES.CRUDE"
        }],
        "views": []

    }, {
        "root_path": "MIPK.DORAS.COMMODITIES.CRUDE",
        "root_name": "CRUDE",
        "directories": [],
        "views": [{
                "view_name": "CM.tangeant"
            },
            {
                "view_name": "CM.astral"
            },
            {
                "view_name": "CM.forking"
            }
        ]
    }]

}

当前代码:

import logging
import copy
import time

def fetch_resources(input_resources_list):
    '''
    :return: Json list of dictionaries each dictionary element containing:
    Directory name, directory path, list of sub-directories, list of views

    resource_list is a flattened list produced by a database walk function
    '''

    start = time.time()
    resources = {
        'contents': [{}]
    }

    for item in input_resources_list:
        # Parsing list into usable pieces
        components = item.rsplit('\\', 1)

        if len(components) == 1:
            # Handles first element
            root_dict = {'root_path': components[0],
                         'root_name': components[-1],
                         'directories': [],
                         'views': []
                         }
            resources['contents'][0].update(root_dict)
        else:
            #  Enumerate resources in list so search by key value can be done and then records can be appended.
            for ind, content in enumerate(copy.deepcopy(resources['contents'])):

                if resources['contents'][ind]['root_path'] == components[0]:
                    # Directories are upper case, adds a new entry if
                    if clean_item.isupper() :
                        root_dict = {'root_path': components[0],
                                     'root_name': components[-1],
                                     'directories': [],
                                     'views': []
                                     }
                        resources['contents'].append(root_dict)
                        sub_dict = {'subd_path': components[0],
                                    'subd_name': components[-1]}
                        resources['contents'][ind]['directories'].append(sub_dict)
                    elif clean_item.isupper() == False :
                        resources['contents'][ind]['views'] \
                            .append({'view_name':components[-1]})
    print 'It took {}'.format((time.time() - start)*1000) 
    return resources

这适用于小型工作负载(大约 100-500),但不适用于 000 的目标工作负载。

  1. 如何优化时间方法?

  2. 目前,为输入列表中的每个项目重新构建枚举循环,以便按 root_path 键值进行搜索。有没有更简单的方法可以在字典列表中搜索键值并将条目附加到该条目目录和视图列表中?

【问题讨论】:

  • 不要使用嵌套推导,而是使用 for 循环语句编写它,它可能看起来不会那么混乱,或者很难正确。并非所有东西都必须尽可能地密集。
  • 但与此同时,直接在 walk 中生成您想要的内容可能会更容易,而不是使用 walk 来构建字符串列表,然后尝试解析这些字符串。
  • 另外,如果目录包含子目录,则将目录存储为 dict,但如果它们包含文件,则将其存储为列表。如果一个目录同时包含两者会发生什么?这是一个错误吗?
  • 作为对您上一条评论的回应,它们可以同时包含文件和目录...添加了对输出格式的解释。如果是这种情况,结构应该如何?
  • 您已经完全改变了您要求的结构。同时,您在此处显示的结构与您在评论我的答案时要求的结构完全不同。无论您想要什么格式,您都可以使用我的回答中的基本结构进行构建,但当然有些细节会有所不同,我无法为您提供可以复制和粘贴的代码并处理您可能要求的所有内容.

标签: python json parsing marshalling


【解决方案1】:

在构建这些字符串时,您已经丢弃了大量信息。例如,当您看到 MIPK\/DORAS 时,无法知道它是文件(应该只是父目录列表中的字符串)、叶目录(应该是父目录 dict 中的列表)还是一个中间目录(应该是父目录字典中的字典)。你能做的最好的事情(没有二次嵌套搜索)就是猜测,如果你猜错了,以后再改。

此外,有时您的中间目录甚至不会单独显示,而只是作为后续路径的组件出现,但有时它们确实会出现。因此,有时您必须修复的“猜测”将是根本不存在的目录。

最后,你想要的格式是不明确的,例如,any 可以直接包含文件和目录(应该是字典还是列表),或者什么都不包含(应该是空字典、空列表、还是只是一个字符串?)。

但是,事情似乎有条不紊,模棱两可的情况似乎并没有真正出现。如果我们可以同时依赖这两者,我们可以通过查看它是前缀还是下一个条目来判断某个东西是否是目录。然后我们就可以读取叶子并将它们放入一个包含 0 个或多个嵌套字典的列表中的字符串集合中。

所以,首先,我们要迭代相邻的路径对,以便更容易做到“它是下一个值的前缀吗?”检查:

output = {}

it1, it2 = itertools.tee(paths)
next(it2)
pairs = itertools.zip_longest(it1, it2, fillvalue='')
for path, nextpath in pairs:
    if nextpath.startswith(path):
        continue

现在,我们知道path 是一片叶子,所以我们需要找到将其附加到的列表,如果需要创建一个,这可能意味着在此过程中递归地创建字典:

components = path.split(r'\/')
d = output
for component in components[:-2]:
    d = d.setdefault(component, {})
d.setdefault(components[-2], []).append(components[-1])

我没有对此进行测试,但它应该为非歧义输入做正确的事情,但是如果任何目录同时包含文件和子目录,或者任何顶级目录包含文件,或者任何一个,都会引发某种异常其他模棱两可的情况(空目录除外,将被视为文件)。

当然,这有点丑陋,但这是解析丑陋格式所固有的,这种格式依赖于许多特殊情况规则来处理否则会模棱两可的内容。

【讨论】:

  • 更新了问题以更准确地反映我想要的内容。每个目录将包含以下字段:namedirectoriesviews。其中目录包含子目录和视图只包含视图的id。那么如何将字典分配给这些标题呢?你能解释一下你提供的代码的流程吗?我没有关注
猜你喜欢
  • 1970-01-01
  • 2021-08-31
  • 2018-11-09
  • 1970-01-01
  • 2019-02-04
  • 2011-08-27
  • 1970-01-01
相关资源
最近更新 更多