如何从此列表中获得所有子孙...

时间:2020-05-02 14:26:07

标签: python python-3.x tree nested

我有items个亲子关系。每个孩子都认识自己的父母,但父母不了解自己的孩子或孙子:

items = [
  {'id': 1, 'parent': None},
  {'id': 2, 'parent': 1},
  {'id': 3, 'parent': 2},
  {'id': 4, 'parent': None},
  {'id': 5, 'parent': 4},
] 

我正在尝试构建一个包含所有项目ID的字典,其中包含其所有子代,孙子等等的列表:

all_children_of_items = {
  1: [2, 3],  # 2 = child, 3 = grandchild
  2: [3],
  3: [],
  4: [5],
  5: [6]
}

我目前的做法只考虑孩子,而不考虑孙子:

all_children_of_items = {}
while True:
  change = False
  for item in items:
    if item['id'] not in all_children_of_items:
      all_children_of_items[item['id']] = []
    if item['parent']:
      if item['parent'] not in all_children_of_items:
        all_children_of_items[item['parent']] = []
      if item['id'] not in all_children_of_items[item['parent']]:
        all_children_of_items[item['parent']].append(item['id'])
  if not change:
    break

当前结果:

{
  1: [2], 
  2: [3], 
  3: [], 
  4: [5], 
  5: []
}

有什么主意吗?预先感谢!

5 个答案:

答案 0 :(得分:1)

您可以尝试以下方法:

tree = {}

for item in items:
    parent = item['id']
    child = [it['id'] for it in items if it['parent'] == parent]
    grandchild = [it['id'] for c in child for it in items if it['parent'] == c]
    tree[parent] = [*child, *grandchild]

print(tree)

输出: {1: [2, 3], 2: [3], 3: [], 4: [5], 5: []}

我看不到5的孩子是6,所以我的代码也没有。

可以对代码进行进一步优化,并针对更一般的用例进行修改。如果您认为合适,我会留给您。

编辑:

针对:

items = [{'id': 1, 'parent': None},
 {'id': 2, 'parent': 1},
 {'id': 3, 'parent': 2},
 {'id': 4, 'parent': 3},
 {'id': 5, 'parent': 4}]

代码:

def nepotism(parent):
    lineage = []
    def recurs(parent):
        for item in items:
            if item['parent'] == parent:
                possible_parent = item['id']
                lineage.append(possible_parent)
                recurs(possible_parent)
    recurs(parent)
    return lineage

tree = dict([(item['id'], nepotism(item['id'])) for item in items])
print(tree)

输出:

{1: [2, 3, 4, 5], 2: [3, 4, 5], 3: [4, 5], 4: [5], 5: []}

答案 1 :(得分:1)

首先,您的while循环无用,因为您总是会在循环结束时中断。

要获得孙子孙,您需要复制

    if item['parent']:
      if item['parent'] not in all_children_of_items:
        all_children_of_items[item['parent']] = []
      if item['id'] not in all_children_of_items[item['parent']]:
        all_children_of_items[item['parent']].append(item['id'])

检查自身内部以处理祖父母。如果要泛化代码以处理任何级别,则需要为要处理的每个其他级别继续在自身内部复制该块。

如果可能需要这种概括,那么递归方法将是最简单的方法,例如以下代码,其中还包括一个预处理步骤,以简化和优化后面的代码:

def getChildrenRecursive(items, maxLevel):
    ''' Returns a dict of item IDs and a list of their children up to maxLevel deep. '''
    itemsByID = {}
    result = {}
    for item in items:
        result[item['id']] = []
        itemsByID[item['id']] = item

    for item in items:
        walkParents(result, item, itemsByID, item['id'], 1, maxLevel)

    return result

def walkParents(result, item, items, idToAdd, level, maxLevel):
    if level > maxLevel:
        return

    parent = item['parent']
    if parent is None:
        return

    result[parent].append(idToAdd)
    parentItem = items[parent]
    walkParents(result, parentItem, items, idToAdd, level + 1, maxLevel)

请注意,该代码可以很容易地转换为非递归版本,因为它仅使用尾部调用,但我将其留给读者练习。

仅仅为了抚养孩子和孙子,就这样称呼:

items = [
  {'id': 1, 'parent': None},
  {'id': 2, 'parent': 1},
  {'id': 3, 'parent': 2},
  {'id': 4, 'parent': None},
  {'id': 5, 'parent': 4},
]
getChildrenRecursive(items, 2) # Returns the result. The number is the maximum number of steps to walk.

答案 2 :(得分:1)

这是另一种解决方案:

items = [
  {'id': 1, 'parent': None},
  {'id': 2, 'parent': 1},
  {'id': 3, 'parent': 2},
  {'id': 4, 'parent': 3},
  {'id': 5, 'parent': None},
]

families = {item['id']: [] for item in items}


def crawl_relations(relatives):
    if len(relatives) and len(families[relatives[0]]) and families[relatives[0]][0] != relatives[-1]:
        crawl_relations(families[relatives[0]])
        relatives += families[relatives[0]]


for item in items:
    if item['parent'] is not None:
        families[item['parent']].append(item['id'])

for relatives in families.values():
    crawl_relations(relatives)

print(families)

答案 3 :(得分:1)

您在这里:

parents_of_children = {item['id']: item['parent'] for item in items}

def find_ancestors(child: int) -> Tuple[int, List[int]]:
  def _find_ancestors(_id: int, family: List[int]):
    if not parents_of_children[_id]:
      return child, family
    else:
      return _find_ancestors(parents_of_children[_id], family + [parents_of_children[_id]])

  return _find_ancestors(child, [])


def find_descendents(id: int, ancestors: List[Tuple[int, List[int]]]) -> List[int]:
  family_trees = [[child] + ancestors_of_child for child, ancestors_of_child in
                  ancestors if id in ancestors_of_child]
  return [child for child in
          takewhile(lambda i: i != id, max(family_trees) if family_trees else [])][::-1]


ancestors = [find_ancestors(child) for child in parents_of_children]
descendents = {id: find_descendents(id, ancestors) for id, _ in ancestors}
print(descendents)

答案 4 :(得分:0)

您可以尝试使用:

if not change:
    continue

break只会中断while循环的迭代。