简体   繁体   English

如何将列表的嵌套字典(带有字典)转换为单个字典列表?

[英]How do I turn a nested dict of lists (with dicts) into a single list of dicts?

I would like to convert a pretty complicated dict into a single list with dicts (like a SQL join).我想将一个非常复杂的字典转换为一个带有字典的列表(如 SQL 连接)。

Input:输入:

{
    'company': 'A',
    'employee': [
        {'name': 'John', 'skills': ['python', 'java']},
        {'name': 'Mary', 'skills': ['web', 'databases']}
    ]
}

Output: Output:

[
    {'company': 'A', 'employee': {'name': 'John', 'skills': 'python'}},
    {'company': 'A', 'employee': {'name': 'John', 'skills': 'java'}},
    {'company': 'A', 'employee': {'name': 'Mary', 'skills': 'web'}},
    {'company': 'A', 'employee': {'name': 'Mary', 'skills': 'databases'}},
]

I would like to be able to add more nested lists aswell as adding more companies etc... So hardcoding the levels of dictionaries and lists is not an option.我希望能够添加更多嵌套列表以及添加更多公司等......所以硬编码字典和列表的级别不是一种选择。 Recursion seems to be my only option.递归似乎是我唯一的选择。

Does anyone have any pointers how to go around doing this?有没有人有任何指示如何 go 这样做?

For the first level I came up with this, but I'm running into trouble when I start calling the flatten function recursively对于第一级,我想出了这个,但是当我开始递归调用 flatten function 时遇到了麻烦


def flatten(input):
    output = []
    for item in input:
        if isinstance(input[item], list):
            for subitem in input[item]:
                copy = input
                # Doesnt work
                # copy[item] = flatten(subitem)

                # Only works for the first layer
                copy[item] = subitem
                output.append(copy.copy())
    return output
print(flatten(start))

This code works for the first level (adding a new record for each employee) but calling flatten recursivly makes the output return an empty list此代码适用于第一级(为每个员工添加新记录),但递归调用 flatten 会使 output 返回一个空列表

Assuming there's always exactly one value in the input dict that is a list, you can find the key with the list value first, and then return a list of the current dict with that key's value replaced with each of the value recursively flattened, until the the value is no longer a dict:假设输入 dict 中始终只有一个值是一个列表,您可以先找到具有列表值的键,然后返回当前 dict 的列表,并将该键的值替换为递归展平的每个值,直到该值不再是一个字典:

def flatten(d):
    if isinstance(d, dict):
        key, lst = next((k, v) for k, v in d.items() if isinstance(v, list))
        return [{**d, **{key: v}} for record in lst for v in flatten(record)]
    else:
        return [d]

flatten(d) returns (given the sample input dict as d ): flatten(d)返回(给定样本输入 dict 为d ):

[{'company': 'A', 'employee': {'name': 'John', 'skills': 'python'}},
 {'company': 'A', 'employee': {'name': 'John', 'skills': 'java'}},
 {'company': 'A', 'employee': {'name': 'Mary', 'skills': 'web'}},
 {'company': 'A', 'employee': {'name': 'Mary', 'skills': 'databases'}}]

Or if there can be a level in the dict where there isn't a value that is a list, in which case the call to the next function with the code above would produce a StopIteration , you can catch that exception and return the input dict as is in a list, and since that's the same behavior when the input isn't a dict, you can simply catch the AttributeError exception (produced when the given object has no items attribute) too to handle both scenarios with the same handler code:或者,如果 dict 中可能存在一个没有列表值的级别,在这种情况下,使用上面的代码调用next function 将产生一个StopIteration ,您可以捕获该异常并返回输入 dict就像在列表中一样,并且由于当输入不是字典时这是相同的行为,您可以简单地捕获AttributeError异常(当给定的 object 没有items属性时产生)也可以使用相同的处理程序代码处理这两种情况:

def flatten(d):
    try:
        key, lst = next((k, v) for k, v in d.items() if isinstance(v, list))
    except (StopIteration, AttributeError):
        return [d]
    return [{**d, **{key: v}} for record in lst for v in flatten(record)]

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

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