如何屏蔽Python 3嵌套字典以返回仅包含特定项的新字典?

时间:2017-09-19 23:46:43

标签: python dictionary iterator

我正在使用API​​的几个端点,它返回的数据非常详细。我想将这些数据的一部分提供给其他地方的另一段代码。

假设我有几个这样的词典(我计划循环并过滤):

asset = {
    'id': 1,
    'name': 'MY-PC',
    'owner': 'me',
    'location': 'New York City',
    'model': {
        'id': 1,
        'name': 'Surface',
        'manufacturer': {
            'id': 1,
            'name': 'Microsoft'
        }
    }
}

我想创建一个将该字典带入的函数,以及一个“掩码”,它将用于创建仅允许的项目的新字典。这可能是一个示例掩码(但是,我可以使用任何格式使得结果代码最简洁):

mask = {
    'id': True,
    'name': True,
    'model': {
        'id': True,
        'name': True,
        'manufacturer': {
            'name': True
        }
    }
}

然后该函数应该返回:

mask = {
    'id': 1,
    'name': 'MY-PC',
    'model': {
        'id': 1,
        'name': 'Surface',
        'manufacturer': {
            'name': 'Microsoft'
        }
    }
}

Python 3中是否已经内置了有助于此的内容?看起来如果我必须手动执行此操作,它会很快变得非常难看。我发现itertools.compress,但这似乎是列表,不会处理字典的复杂性。

3 个答案:

答案 0 :(得分:3)

您可以通过仅选择主要字典中对应的值来从掩码中递归构建新的dict:

def prune_dict(dct, mask):
    result = {}
    for k, v in mask.items():
        if isinstance(v, dict):
            value = prune_dict(dct[k], v)
            if value: # check that dict is non-empty
                result[k] = value
        elif v:
            result[k] = dct[k]
    return result

print(prune_dict(asset, mask))
{'id': 1,
'model': {'id': 1, 'manufacturer': {'name': 'Microsoft'}, 'name': 'Surface'},
'name': 'MY-PC'}

答案 1 :(得分:2)

这是一个使用递归的好机会,这里有一些我没有测试的示例代码:

def copy(asset, result, mask):
    for key_name, value in mask.items():
        if value == True:
            result[key_name] = asset[key_name]
        else:
            result[key_name] = x = {}
            copy(asset[key_name], x, value)

y = {}
copy(asset, y, mask)

答案 2 :(得分:0)

这可能是一个递归函数。此外,对于面具,我建议使用以下格式:mask = ["id", "name", "model.id", "model.name", "model.manufacturer.name"]

然后,您首先只保留在掩码中命名的条目:

def filterstage1(dictionary, mask):
    result = {}
    for key in dictionary:
        if isinstance(dictionary[key], dict):
            newmask = [maskname[mask.find(".") + 1:] for maskname in mask if maskname.startswith(key + ".")]
            result[k] = filterstage1(dictionary[key], newmask)
        elif key in mask:
            result[key] = dictionary[key]
    return result

然后,根据您是否要删除不在掩码中且没有子元素的子词典,您可以包括第二阶段:

def filterstage2(dictionary, mask):
    result = {}
    for key in dictionary:
        if not (isinstance(dictionary[key], dict) and dictionary[key] == {} and key not in mask):
            result[key] = dictionary[key]

最终代码:filterstage2(filterstage1(dictionary, mask), mask)。如果您愿意,可以将两个阶段组合在一起。