【问题标题】:Get parent of nested array in recursive loop在递归循环中获取嵌套数组的父级
【发布时间】:2026-01-15 05:00:02
【问题描述】:

我正在尝试循环浏览 PDF 书签并将它们保存在一个对象中,包括它们的父书签。

感谢 PyPDF4,我可以将这些书签作为数组中的数组获取。

例如:

[]
 0: Root 1
 1: []
     0: Child layer 1.1
     1: Child layer 1.2
     2: []
         0: Child layer 1.2.1
         1: Child layer 1.2.2
         1: Child layer 1.2.3
 2: Root 2

由于这些数组因文件而异,我不知道这个数组是如何构成的。所以我最终选择了一个递归函数来保存它们。

功能

def __iterate_through_bookmarks(outlines, bookmarks, layer = 0, parent = None):
  layer += 1
  print(layer)

  if isinstance(outlines, list): 
    for item in outlines:
      __iterate_through_bookmarks(item, bookmarks, layer, parent)
    return bookmarks
    

  print(outlines.title)
  bookmarks.append(bookmark(outlines, parent))

我做了一些实验,但我无法做到。但是看到函数中的层计数器如何正确处理层给了我希望,这是可能的。

图层输出:

1
2
Root 1
2
3
Child layer 1.1
3
Child layer 1.2
3
4
Child layer 1.2.1
4
Child layer 1.2.2
4
Child layer 1.2.3
2
Root 2

由于递归函数的工作方式,子层 1.x 具有相同的层计数器。但是,我找不到如何将其(相同)父级保存在对象中的解决方案。

最终目标是返回一个带有书签对象的数组。存储大纲的标题、位置和父级的类。

有人有什么建议吗?

【问题讨论】:

  • 您期望什么类型的输出?任何样品

标签: python arrays python-3.x recursion


【解决方案1】:

带有明确的“儿童”字段

递归浏览嵌套的字典列表,其中每个字典都有一个“名称”和一个“子”字段。

nested_list = [
  {'name': 'act IV',
   'children': [{'name': 'scene 4',
                 'children': [{'name': 'dual seduction',
                               'children': []},
                             ]}
               ]},
  {'name': 'act I',
   'children': [{'name': 'scene 2',
                 'children': [{'name': 'inconstancy praise',
                               'children': []},
                              {'name': 'sganarelle monologue',
                               'children': []}
                             ]}
               ]},
]

def get_list_of_bookmarks(nested_list, parent_name=None):
  bookmark_list = []
  for bookmark in nested_list:
    bookmark_list.append({'name': bookmark['name'], 'parent': parent_name})
    bookmark_list.extend(get_list_of_bookmarks(bookmark['children'], parent_name=bookmark['name']))
  return bookmark_list

print(get_list_of_bookmarks(nested_list))
# [{'name': 'act IV', 'parent': None},
#  {'name': 'scene 4', 'parent': 'act IV'},
#  {'name': 'dual seduction', 'parent': 'scene 4'},
#  {'name': 'act I', 'parent': None},
#  {'name': 'scene 2', 'parent': 'act I'},
#  {'name': 'inconstancy praise', 'parent': 'scene 2'},
#  {'name': 'sganarelle monologue', 'parent': 'scene 2'}]

或者,如果您想存储对父级的引用,而不仅仅是父级的名称:

def get_list_of_bookmarks(nested_list, parent=None):
  bookmark_list = []
  for bookmark_with_children in nested_list:
    bookmark_with_parent = {'name': bookmark_with_children['name'], 'parent': parent}
    bookmark_list.append(bookmark_with_parent)
    bookmark_list.extend(get_list_of_bookmarks(bookmark_with_children['children'], parent=bookmark_with_parent))
  return bookmark_list

print(get_list_of_bookmarks(nested_list))
# [{'name': 'act IV', 'parent': None},
# {'name': 'scene 4', 'parent': {'name': 'act IV', 'parent': None}},
# {'name': 'dual seduction', 'parent': {'name': 'scene 4', 'parent': {'name': 'act IV', 'parent': None}}},
# {'name': 'act I', 'parent': None},
# {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}},
# {'name': 'inconstancy praise', 'parent': {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}}},
# {'name': 'sganarelle monologue', 'parent': {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}}}]

请注意,print 的输出看起来非常冗余且充满了副本,但事实并非如此。每个书签都包含对其父级的引用;它不包含其父级的副本。

没有明确的 'children' 字段:顺序相关

现在假设您没有明确的'children' 字段,并且您的嵌套列表只是一个列表列表;子列表被认为是前一个元素的子元素。

nested_list = [
  'act IV',
  ['scene 4', ['dual seduction']],
  'act I',
  ['scene 2', ['inconstancy praise', 'sganarelle monologue']],
]

我们可以从相关问题中汲取灵感:Flatten an irregular list of lists

from collections.abc import Iterable

def flatten_with_depth(l, depth=0):
    for el in l:
        if isinstance(el, Iterable) and not isinstance(el, (str, bytes)):
            yield from flatten_with_depth(el, depth=depth+1)
        else:
            yield (el, depth)
# flatten_with_depth() adapted from flatten() at https://*.com/a/2158532/3080723

print(list(flatten_with_depth(nested_list)))
# [('act IV', 0), ('scene 4', 1), ('dual seduction', 2), ('act I', 0), ('scene 2', 1), ('inconstancy praise', 2), ('sganarelle monologue', 2)]

现在书签列表是扁平的,每个书签都有它的深度。深度为depth 的给定书签的父级是深度为depth-1 的最近的先前书签。轻松找到父母的一种有效方法是在当前分支中维护一堆祖先。

def get_parents_knowing_depths(list_with_depths):
  ancestor_stack = []
  result = []
  for (bookmark_name, depth) in list_with_depths:
    if depth == 0:
      bookmark = {'name': bookmark_name, 'parent': None}
      ancestor_stack = [bookmark]
    else:
      while len(ancestor_stack) > depth:
        ancestor_stack.pop()
      bookmark = {'name': bookmark_name, 'parent': ancestor_stack[-1]}
      ancestor_stack.append(bookmark)
    result.append(bookmark)
  return result

print(get_parents_knowing_depths(flatten_with_depth(nested_list)))
# [{'name': 'act IV', 'parent': None},
#  {'name': 'scene 4', 'parent': {'name': 'act IV', 'parent': None}},
#  {'name': 'dual seduction', 'parent': {'name': 'scene 4', 'parent': {'name': 'act IV', 'parent': None}}},
#  {'name': 'act I', 'parent': None},
#  {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}},
#  {'name': 'inconstancy praise', 'parent': {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}}},
#  {'name': 'sganarelle monologue', 'parent': {'name': 'scene 2', 'parent': {'name': 'act I', 'parent': None}}}]

【讨论】:

  • 是的,这就是我想要实现的目标。很好的例子,但我不知道如何将它与数组合并,因为我没有像您的列表中那样保证的“儿童”标签。
  • @ImTryingOkay 我在答案中添加了一个新部分,这是否符合您想要的更好一点?
  • 是的,它做到了。我不得不做一些调整:在 flatten_with_depth 我添加了 if isinstance(l, list): 在 for 循环之前,因为否则 Root1 元素会导致异常,因为它是不可迭代的。在 get_parents_knowing_depths 我编辑了 if depth == 0: 到 if depth == 1: 的 if。因为深度 1 是 flatten_with_depth 生成的最低深度。非常感谢