展平复杂的字典,同时使用 dot 作为分隔符压缩键

flatten a complex dict, while compressing keys using dot as a separator

提问人:shachar0n 提问时间:5/18/2023 最后编辑:shachar0n 更新时间:5/21/2023 访问量:105

问:

我正在尝试想出一个 python3 函数(当然,使用现有的库也可以使用)来扁平化此输入:

{
  'key1': 1, 
  'key2dict': {'subkey1': 1, 'subkey2': 2},
  'key3listOfDict': [
    {'subkey3': 3, 'subkey4': 4},
    {'subkey5': 5, 'subkey6': 6}
  ],
  'key4nestedListOfDict': [
    {
      'subkey7': 7, 
      'subkeyNested': [
        {'subkey8': 8}, 
        {'subkey9': 9}
      ]
    }
  ]
}

进入这个:

[
  {
    'key1': 1, 
    'key2dict.subkey1': 1,
    'key2dict.subkey2': 2,
    'key3listOfDict.subkey3': 3,
    'key3listOfDict.subkey4': 4,
    'key4nestedListOfDict.subkey7': 7,
    'key4nestedListOfDict.subkeyNested.subkey8': 8,
  },
  {
    'key1': 1, 
    'key2dict.subkey1': 1,
    'key2dict.subkey2': 2,
    'key3listOfDict.subkey3': 3,
    'key3listOfDict.subkey4': 4,
    'key4nestedListOfDict.subkey7': 7,
    'key4nestedListOfDict.subkeyNested.subkey9': 9,
  },
  {
    'key1': 1, 
    'key2dict.subkey1': 1,
    'key2dict.subkey2': 2,
    'key3listOfDict.subkey5': 5,
    'key3listOfDict.subkey6': 6,
    'key4nestedListOfDict.subkey7': 7,
    'key4nestedListOfDict.subkeyNested.subkey8': 8,
  },
  {
    'key1': 1, 
    'key2dict.subkey1': 1,
    'key2dict.subkey2': 2,
    'key3listOfDict.subkey5': 5,
    'key3listOfDict.subkey6': 6,
    'key4nestedListOfDict.subkey7': 7,
    'key4nestedListOfDict.subkeyNested.subkey9': 9,
  }
]

我遇到的主要挑战是正确处理对象列表和嵌套对象列表。 我自己研究并尝试了一些,但这些方法没有按预期工作。

任何帮助将不胜感激!

作为记录,到目前为止我有这些(没有正确地完成工作..):

from collections.abc import MutableMapping

def flatten(dictionary, parent_key='', separator='.'):
    items = []
    for key, value in dictionary.items():
        new_key = parent_key + separator + key if parent_key else key
        if isinstance(value, MutableMapping):
            items.extend(flatten(value, new_key, separator=separator).items())
        else:
            items.append((new_key, value))
    return dict(items)

def flatten_handle_lists(row):
    rows = []
    for i, (key, value) in enumerate(row.items()):
        if isinstance(value, list):
            for j, v in enumerate(value):
                expansion_row = dict(row)
                del expansion_row[key]
                expansion_row.update(flatten(v, key, '.'))
                rows.append(expansion_row)
    return rows
python json 递归 套扁平

评论

1赞 Patrick Haugh 5/18/2023
输出中有一些右括号,看起来不属于它们。你想要字典数组的乘积,我说得对吗?其他值的数组呢?]
0赞 Barmar 5/18/2023
当您有一个词典列表时,它们的所有键总是不同的吗?
0赞 shachar0n 5/18/2023
是的,你是对的@PatrickHaugh,谢谢。我更新了输出。[> 你想要字典数组的乘积,我说得对吗?是的,在某种程度上就像我放的例子一样有意义。[>其他值的数组呢?我不希望数组中的对象以外的任何东西出现问题。根据我的经验,这些是扁平的,它们的键压缩得不那么复杂。
0赞 shachar0n 5/18/2023
@Barmar - [> 当你有一个字典列表时,它们的所有键总是不同的吗? 嗯,并不总是如此,但我没有任何约束可以使它们始终相同(从计数和类型的角度来看),因此我打算让我的例子处理我可能期望的最复杂的情况。
0赞 Barmar 5/18/2023
现在我更仔细地查看了所需的结果,我发现这并不重要,因为它们最终出现在最终列表的不同元素中。

答:

1赞 yut23 5/18/2023 #1

将其分为两个步骤要简单得多:首先处理列表,然后再进行字典扁平化。

import itertools
from collections.abc import Mapping


def explode_nested_lists(dictionary):
    """
    Turn a nested dictionary with lists representing different possible subtrees
    into a list of dictionaries for each combination of the subtrees.
    """
    options = {}
    for k, v in dictionary.items():
        if isinstance(v, list):
            options[k] = list(itertools.chain(*map(explode_nested_lists, v)))
        elif isinstance(v, Mapping):
            options[k] = explode_nested_lists(v)
        else:
            options[k] = [v]
    keys = list(options.keys())
    return [
        dict(zip(keys, vals)) for vals in itertools.product(*(options[k] for k in keys))
    ]


def flatten_keys(dictionary, separator="."):
    """Flatten a nested dictionary by joining keys with a separator."""
    result = {}
    for key, value in dictionary.items():
        if isinstance(value, Mapping):
            result.update(
                (key + separator + k, v)
                for k, v in flatten_keys(value, separator).items()
            )
        else:
            result[key] = value
    return result


def flatten(dictionary, separator="."):
    return [flatten_keys(d, separator) for d in explode_nested_lists(dictionary)]

评论

0赞 shachar0n 5/18/2023
是的,我明白你的意思......我被困在尝试先进行扁平化,然后再取消嵌套/分解这些列表。但你的方法听起来更好,看起来很有希望。我现在正在用你的代码执行一些测试,但我认为这就是我一直在寻找的确切答案。谢谢!将很快更新..