How to get all children, grandchildren, … from this list?

本秂侑毒 提交于 2020-05-16 05:55:06

问题


I've items in a parent-child-relation. Every child knows its parent but a parent doesn't know its children nor its grandchildren:

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

I'm trying to build a dict which includes all item ids with a list of all its children, grandchildren and so on:

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

My current approach only considers the children, not the grandchildren:

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

Current result:

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

Any idea? Thanks in advance!


回答1:


You may try this:

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)

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

I do not see how 5 has 6 as child, so neither does my code.

The code can be further optimized, and modified for more general use cases. I leave that to you, as you see fit.

EDIT:

For:

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

Code:

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)

Output:

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



回答2:


First off, your while loop is useless as you will always break at the end of it.

In order to get the grandchildren, you would need to duplicate the

    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'])

check inside of itself to handle the grandparent. If you wanted to generalize your code to handle any level, you would need to keep duplicating that block inside of itself for each additional level you wanted to handle.

If such generalization may be required, a recursive approach would be easiest, such as the following code, which also includes a pre-process step to simplify and optimize the later code:

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)

Note that the code can be converted to a non-recursive version fairly easily as it does only use a tail-call, but I will leave that as an exercise to the reader.

For just getting the children and grand-children, it would be called like:

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.



回答3:


Here is another solution:

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)



回答4:


Here you go:

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)



回答5:


You can try using:

if not change:
    continue

break will simply break the iteration from the while loop.



来源:https://stackoverflow.com/questions/61560926/how-to-get-all-children-grandchildren-from-this-list

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!