使用嵌套 for 循环扁平化数据结构的替代方法

Alternatives to using nested for loops to flatten data structure

提问人:Bella Grubb 提问时间:8/19/2023 最后编辑:Bella Grubb 更新时间:8/19/2023 访问量:90

问:

我有一个嵌套的数据结构:字典中包含许多级别的字典列表。例如:

{‘L1’:
    ‘L2’:
        {‘L3’: [{‘a’: 1, ‘b’: 2}, {‘c’: 1, ‘d’: 2}]}
  Etc …
}

我想要一个输出,它使列表的内部列表扁平化,每个级别的外部元素。例如

L1、L2、L3、a、1、b、2

L1、L2、L3、c、1、d、2

等。。。

除了使用嵌套的 for 循环之外,有没有一种更“pythonic”和更有效的方法来实现结果?

嵌套 for 循环,当数据结构较大时,它可以工作但速度很慢

Python 循环 嵌套 的数据结构

评论

0赞 Barmar 8/19/2023
请记住在复制和粘贴代码时关闭“智能引号”。
0赞 Barmar 8/19/2023
[‘a’: 1, ‘b’: 2]无效。 语法只允许在字典中使用,而不允许在列表中使用。key: value
1赞 Barmar 8/19/2023
检查它是否有任何功能可以执行您想要的操作。itertools
1赞 8/19/2023
itertools.chain() 可能是你要找的
0赞 juanpa.arrivillaga 8/19/2023
“嵌套 for 循环,当数据结构很大时,它有效但速度很慢” 如果你想要每个元素,就没有选择遍历每个元素。嵌套循环本身并不慢,这是从算法上做你想做的事情的最快方法。

答:

1赞 Sash Sinha 8/19/2023 #1

考虑使用递归函数:

from itertools import chain
from typing import Iterator, Optional, Union


def flatten(
    d: dict,
    keys: Optional[list[Union[str, int]]] = None
) -> Iterator[list[Union[str, int]]]:
  """Recursively flattens a nested dictionary structure into a list of lists.

     Args:
       d: The input nested dictionary to flatten.
       keys: A list of keys from the parent dictionaries. 

     Yields:
       Iterator[list[Union[str, int]]]: A list containing the flattened elements.
  """
  if keys is None:
    keys = []
  for k, v in d.items():
    new_keys = keys + [k]
    if isinstance(v, dict):
      yield from flatten(v, new_keys)
    elif isinstance(v, list):
      for item in v:
        flattened_items = chain.from_iterable(item.items())
        yield new_keys + list(flattened_items)
    else:
      yield new_keys + [v]


def main() -> None:
  data = {'L1': {'L2': {'L3': [{'a': 1, 'b': 2}, {'c': 1, 'd': 2}]}}}
  for item in flatten(data):
    print(*item)


if __name__ == '__main__':
  main()

输出:

L1 L2 L3 a 1 b 2
L1 L2 L3 c 1 d 2

评论

0赞 juanpa.arrivillaga 8/19/2023
为什么?这更快吗?
1赞 Alain T. 8/19/2023 #2

如果您关心的是嵌套循环的性能,您可能需要考虑为数据结构使用不同的模型,因为无论您如何处理嵌套,都会发生某种形式的嵌套。

如果问题出在不同的级别数量上,那么递归可能是你最好的选择(尽管它会比嵌套循环慢)。

下面是一个递归生成器,它将输出每一行,而无需创建结构的完整(扁平化)副本:

def flatItems(d,path=[]):
    if isinstance(d,list):
        for k,v in enumerate(d,1):
            yield from flatItems(v,path+[k])
    elif isinstance(d,dict):
        for k,v in d.items():
            yield from flatItems(v,path+[k])
    else:
        yield path + [d]

输出:

data = {'L1': {'L2': {'L3': [{'a': 1, 'b': 2}, {'c': 1, 'd': 2}]}}}

print(*flatItems(data),sep="\n")

['L1', 'L2', 'L3', 1, 'a', 1]
['L1', 'L2', 'L3', 1, 'b', 2]
['L1', 'L2', 'L3', 2, 'c', 1]
['L1', 'L2', 'L3', 2, 'd', 2]

请注意,根据您的示例,我对列表使用了基于 1 的索引。Python 的列表索引是从零开始的,您可能也想重新审视一下。