You can do a depth first search. Just keep track of which items you've seen in a set. Then yield each one you haven't seen and yield from the result of recursive call:
def get_all_descendants(t, d, seen=None):
if seen is None:
seen = set([t])
for item in d[t]:
if item not in seen:
seen.add(item)
yield item
yield from get_all_descendants(item, d, seen)
list(get_all_descendants('charges', d))
This will give you this list:
['mview',
'status',
'accounts',
'nodate',
'tainlog',
'remfile',
'retlog',
'retfile',
'bb',
'payfile',
'aa',
'snaps',
'raw',
'pview',
'balance']
Answer from Mark on Stack OverflowYou can do a depth first search. Just keep track of which items you've seen in a set. Then yield each one you haven't seen and yield from the result of recursive call:
def get_all_descendants(t, d, seen=None):
if seen is None:
seen = set([t])
for item in d[t]:
if item not in seen:
seen.add(item)
yield item
yield from get_all_descendants(item, d, seen)
list(get_all_descendants('charges', d))
This will give you this list:
['mview',
'status',
'accounts',
'nodate',
'tainlog',
'remfile',
'retlog',
'retfile',
'bb',
'payfile',
'aa',
'snaps',
'raw',
'pview',
'balance']
I think @Mark's answer can be improved. Creating an inner loop means we will not check if seen is a set on each iteration. And moving the if outside of the for loop means you can skip many unnecessary checks on child nodes when the parent node has already been seen. The loop also uses the closure property so d and seen do not need to be passed to every recursive call -
def dfs(t, d):
seen = set() # unconditional
def loop(t): # single parameter
if t not in seen: # if outside of loop
seen.add(t)
yield t
for item in d[t]:
yield from loop(item)
return drop(loop(t), 1) # exclude starting node from result
This depends on a generic drop helper which removes n items from an iterable -
def drop(it, n):
for _ in range(n):
next(it, None)
return it
The output is identical -
for node in dfs("charges", d):
print(node)
mview
status
accounts
nodate
tainlog
remfile
retlog
retfile
bb
payfile
aa
snaps
raw
pview
balance
The data preparation step is a little hideous, because your IDs are starting from one we need a blocker element None at position 0:
original = [(1, 'A', 0),
(2, 'B', 1),
(3, 'C', 1),
(4, 'D', 2),
(5, 'E', 2),
(6, 'F', 3),
(7, 'G', 2),
(8, 'H', 6),
(9, 'I', 4)]
ids,data,parents = zip(*original)
data =[None]+list(data)
parents = [None]+list(parents)
This code will work once you get your input data in the right format:
ids = range(1,10)
parents = [None,0,1,1,2,2,3,2,6,4]
data =[None,"A","B","C","D","E","F","G","H","I"]
def get_parents(node):
if not parents[node]:
return data[node]
return get_parents(parents[node])+data[node]
for id in ids:
print ".".join(list(get_parents(id)))
>>>
A
A.B
A.C
A.B.D
A.B.E
A.C.F
A.B.G
A.C.F.H
A.B.D.I
After a writing out each step on a piece of paper, I was able to come up with the following:
def generate_category_strings(cat_list, parent_item, sent_str, retlist=[]):
"""
Concatenates categories together and returns a list of them.
"""
if parent_item is None:
parent_item = cat_list[0]
retlist.append(parent_item[1])
parent_id = parent_item[0]
parent_name = parent_item[1]
if sent_str is None:
sent_str = parent_name
children = [_x for _x in cat_list if _x[2] == parent_id]
if len(children) == 0:
# base case, no children
return ["{}.{}".format(sent_str, parent_name)]
else:
for child in children:
str_to_send = "{}.{}".format(sent_str, child[1])
retlist.append(str_to_send)
generate_category_strings(cat_list, child, str_to_send, retlist)
return retlist
Running this on the original dataset:
data = [(1, "A", 0), (2, "B", 1), (3, "C", 1), (4, "D", 2),
(5, "E", 2), (6, "F", 3), (7, "G", 2), (8, "H", 6),
(9, "I", 4),
]
a = generate_category_strings(data, None, None)
for item in a:
print(item)
prints:
A
A.B
A.B.D
A.B.D.I
A.B.E
A.B.G
A.C
A.C.F
A.C.F.H
This implementation should work
def get_family_tree(person):
""" return a family tree for a Person object """
children = person.children.all()
if not children:
# this person has no children, recursion ends here
return {'name': person.name, 'children': []}
# this person has children, get every child's family tree
return {
'name': person.name,
'children': [get_family_tree(child) for child in children],
}
Note that this will take as many database calls as there are persons. You can try to fetch all the data into memory if you run into performance issues.
Thinking About Recursion
One way to think about recursion is to start off with the base case - i.e. where the recursion will end. In your case, we know how the family tree looks like if a person has no children:
{
'name': 'FirstPerson',
'children': [],
}
After you have the base case(s), think about the problem where you have to perform the recursion once.
In your case, it would be parents with children, but no grand children. We know how each child's family tree should look - it's just the base case! This leads us to the solution where we return the parent's name, and a list of each child's family tree. Leading to something like:
{
'name': FirstPerson,
'children': [<each element is a child's family tree>]
}
Edit
Django automatically generates reverse relations for a ForeignKey.
class Person(models.Model):
....
parent = models.ForeignKey('self', related_name='children', blank=True, null=True)
p = Person()
p.children.all() # automatically fetch all Person objects where parent=p
See https://docs.djangoproject.com/en/1.9/ref/models/fields/#foreignkey
You should try the package django-mptt as it works great or this purpose:
You can use TreeForeignKey() as the ForeignKey.
You can then add this method to the model to get the objects (or look into the docs I have provided to get the children instead of the parents/ancestors):
def get_category_and_parents(self):
""" Recursively retrieves parent categories (including self) using MPTT """
full_category = self.category.get_ancestors(include_self=True)
return full_category
Github:
https://github.com/django-mptt/django-mptt
Docs:
http://django-mptt.github.io/django-mptt/mptt.models.html#mptt.models.MPTTModel.get_children
Here is a pure XSLT solution -- efficiently using keys (equivalent to hash-tables) and just 23 lines -- the shortest solution so far.
This is also computationally the simplest one -- compare nesting level 1 to nesting level of 4 - 5 ...
This solution is tail-recursive meaning that any good XSLT processor optimizes it with iteration, thus avoiding the possibility of stack-overflow, as the maximum call-stack depth remains constant (1).
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kNodeByChild" match="node" use="@child"/>
<xsl:key name="kNodeByName" match="node" use="@name"/>
<xsl:template match="/*">
<xsl:apply-templates select="node[not(key('kNodeByChild', @name))]"/>
</xsl:template>
<xsl:template match="node[not(key('kNodeByName', @child))]">
<xsl:param name="pParentPath"/>
<xsl:value-of select="concat($pParentPath, @name, ' ---> ', @child, '
')"/>
</xsl:template>
<xsl:template match="node">
<xsl:param name="pParentPath"/>
<xsl:apply-templates select="key('kNodeByName', @child)">
<xsl:with-param name="pParentPath" select="concat($pParentPath, @name, ' ---> ')"/>
</xsl:apply-templates>
</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<nodes>
<node name="Car" child="Engine"/>
<node name="Car" child="Wheel"/>
<node name="Engine" child="Piston"/>
<node name="Engine" child="Carb"/>
<node name="Carb" child="Bolt"/>
<node name="Spare Wheel"/>
<node name="Bolt" child="Thread"/>
<node name="Carb" child="Foat"/>
<node name="Truck" child="Engine"/>
<node name="Engine" child="Bolt"/>
<node name="Wheel" child="Hubcap"/>
</nodes>
The wanted, correct result is produced:
Car ---> Engine ---> Piston
Car ---> Engine ---> Carb ---> Bolt ---> Thread
Car ---> Engine ---> Carb ---> Foat
Car ---> Engine ---> Bolt ---> Thread
Car ---> Wheel ---> Hubcap
Spare Wheel --->
Truck ---> Engine ---> Piston
Truck ---> Engine ---> Carb ---> Bolt ---> Thread
Truck ---> Engine ---> Carb ---> Foat
Truck ---> Engine ---> Bolt ---> Thread
It has been a long time since I did anything with graphs but this should be pretty close if not the most optimal approach:
x = """<?xml version="1.0"?>
<nodes>
<node name="Car" child="Engine"></node>
<node name="Engine" child="Piston"></node>
<node name="Engine" child="Carb"></node>
<node name="Car" child="Wheel"></node>
<node name="Wheel" child="Hubcaps"></node>
<node name="Truck" child="Engine"></node>
<node name="Truck" child="Loading Bin"></node>
<nested>
<node name="Spare Wheel" child="Engine"></node>
</nested>
<node name="Spare Wheel" child=""></node>
</nodes>"""
from lxml import etree
xml = etree.fromstring(x)
graph = {}
nodes = set()
for x in xml.xpath("//node"):
par, child = x.xpath(".//@name")[0], x.xpath(".//@child")[0]
graph.setdefault(par, set())
graph[par].add(child)
nodes.update([child, par])
def find_all_paths(graph, start, end, path=None):
if path is None:
path = []
path = path + [start]
if start == end:
yield path
for node in graph.get(start, []):
if node not in path:
for new_path in find_all_paths(graph, node, end, path):
yield new_path
for n in graph:
for e in nodes:
if n != e:
for path in find_all_paths(graph, n, e):
if path:
print("--> ".join(path))
That would give you:
Engine--> Carb
Engine--> Piston
Car--> Engine
Car--> Wheel
Car--> Wheel--> Hubcaps
Car--> Engine--> Carb
Car--> Engine--> Piston
Spare Wheel--> Engine
Spare Wheel-->
Spare Wheel--> Engine--> Carb
Spare Wheel--> Engine--> Piston
Wheel--> Hubcaps
Truck--> Engine
Truck--> Engine--> Carb
Truck--> Engine--> Piston
Truck--> Loading Bin
I think it might be more helpful to you if I post a working example of how to do this, as opposed to going through where you code is having problems. We might get to the point of understanding a lot faster that way. Your code has the correct idea that it needs to track the depth as it goes. But the only thing it is missing is a sense of nested depth (tree). It only knows the previous node_count, and then its current child count.
My example uses a closure to start the depth tracking object, and then creates an inner function to do the recursive part.
def recurse(box):
boxes = not isinstance(box, (list, tuple)) and [box] or box
depth = [1]
def wrapped(box):
depthStr = '.'.join([str(i) for i in depth])
print "%s %s" % (depthStr, box.name)
depth.append(1)
for child in box.boxItems:
wrapped(child)
depth[-1] += 1
depth.pop()
for box in boxes:
wrapped(box)
depth[0] += 1
Sample output from your examples:
>>> recurse(example)
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
>>> recurse([example, example])
1 Example Box
1.1 Big Box
1.1.1 Normal Box
1.1.2 Friendly Box
1.2 Cool Box
2 Example Box
2.1 Big Box
2.1.1 Normal Box
2.1.2 Friendly Box
2.2 Cool Box
Breaking this down:
We first accept a box argument, and automatically convert it locally to a list, if you had only passed in a single box item. That way you can pass either one box objects, or a list/tuple of them.
depth is our depth tracker. Its a list of ints that we will build up and shrink down as the recursion happens. It starts at 1 for the first item / first level. Over time it can look like this: [1,1,2,3,1] depending on how deep it traverses. This is the major difference between my code and yours. Each recursion has access to this state.
Now we have this inner wrapped function. Its going to take a current box item and print it, and then iterate over its children. We get our print string by joining the current depth list, and then the name.
Every time we drop down into a child list, we add a starting level 1 to our depth list, and when we come out of that child loop, we pop it back off again. For every child in that loop, we increment that last item up.
Outside of that wrapped inner function, we then start the whole thing by looping over our initial boxes, calling wrapped and then incrementing our first level.
The inner wrapped function uses the depth list in a closure. I am willing to bet that others can offer some further improvements on this, but its what I came up with for an example.
Note about the args for the function
We could have also designed recurse to instead take a variable length argument list, instead of checking for a list. It would look like this (and would get rid of that first boxes = check):
def recurse(*boxes):
#boxes will always come in as a tuple no matter what
>>> recurse(example)
>>> recurse(example, example, example)
And if you originally start with a list of box items, you can pass it by doing:
>>> boxes = [example, example, example]
>>> recurse(*example) # this will unpack your list into args
You have two options:
- Keep track of the additional information as an additional parameter in your recursion, e.g.
myRecursiveFunction(..., ancestry=[]) - Have each BoxItem keep track of its parent, whenever it is embedded in a BoxItem (in the
__init__constructor, setchild.parent = selffor each child). This is bad if you plan to have a BoxItem in more than one box.
You can use networkx to solve the problem. Note if you use networkx you don't need the highest columns. The main function to find all paths is all_simple_paths
# Python env: pip install networkx
# Anaconda env: conda install networkx
import networkx as nx
# Create network from your dataframe
#G = nx.from_pandas_edgelist(df, source='parent', target='child',
# create_using=nx.DiGraph)
# For older versions of networkx
G = nx.DiGraph()
for _, (source, target) in df[['parent', 'child']].iterrows():
G.add_edge(source, target)
# Find roots of your graph (a root is a node with no input)
roots = [node for node, degree in G.in_degree() if degree == 0]
# Find leaves of your graph (a leaf is a node with no output)
leaves = [node for node, degree in G.out_degree() if degree == 0]
# Find all paths
paths = []
for root in roots:
for leaf in leaves:
for path in nx.all_simple_paths(G, root, leaf):
paths.append(path)
# Create a new dataframe
out = pd.DataFrame(paths).fillna('')
out.columns = reversed(out.add_prefix('level ').columns)
Output:
>>> out
level 3 level 2 level 1 level 0
0 a b c
1 a b d e
This can be done using the networkx library. You do not need the highest column. It can be deduced from the graph.
import pandas as pd
import networkx as nx
df = pd.DataFrame([ \
('a', 'b', 1), \
('b', 'c', 0), \
('b', 'd', 0), \
('d', 'e', 0)], columns=['parent', 'child', 'highest'])
parents = df.parent.values
childs = df.child
G = nx.DiGraph()
for i in range(len(parents)):
parent = parents[i]
child = childs[i]
if not parent in G.nodes:
G.add_node(parent)
if not child in G.nodes:
G.add_node(child)
G.add_edge(parent, child)
roots = [node for node in G.nodes if G.in_degree(node) == 0]
root = roots[0]
leaves = [node for node in G.nodes if G.out_degree(node) == 0]
all_paths = [list(nx.simple_paths.all_simple_paths(G, root, leaf)) for leaf in leaves]
# Flatten
all_paths = [tuple(path) for paths in all_paths for path in paths]
paths_df = pd.DataFrame(all_paths)
I will leave for you to rename the columns as per your liking.
you should probably use a defaultdictionary for this:
from collections import defaultdict
itemdict = defaultdict(list)
for id, parent_id in itemlist:
itemdict[parent_id].append(id)
then you can recursively print it (with indentation) like
def printitem(id, depth=0):
print ' '*depth, id
for child in itemdict[id]:
printitem(child, depth+1)
Are you saying that each item only maintains a reference to its parents? If so, then how about
def getChildren(item) :
children = []
for possibleChild in allItems :
if (possibleChild.parent == item) :
children.extend(getChildren(possibleChild))
return children
This returns a list that contains all items who are in some way descended from item.
More Python thing than Blender's one, but you can:
import bpy
def find_meshes_recursive( root, levels=10, meshes=None ):
# Initialize the result once
if meshes is None:
meshes = []
def recurse( parent, result, level, levels ):
# Does nothing if level is reached
if level < levels:
# Keeps meshes
if parent.type == 'MESH':
result.append(parent)
# Look over children at next level
for child in parent.children:
recurse( child, result, level + 1, levels )
recurse( root, meshes, 0, levels )
return meshes
root = bpy.context.object
meshes = find_meshes_recursive(root, levels = 10)
print(meshes)
Note that you can also do it in non recursive way:
def find_meshes(root, levels=10, meshes=None):
meshes = [] if meshes is None else meshes
parents = [root]
while levels > 0 and parents:
meshes.extend([obj for obj in parents if obj.type == 'MESH'])
parents = [child for obj in parents for child in obj.children]
levels -= 1
return meshes
Recursive generator.

Test Data.
At its bare basics can walk a tree and yield all objects. List comprehension is used on result to keep or weed out by some condition.
import bpy
context = bpy.context
def walk_children(ob):
yield ob
for child in ob.children:
yield from walk_children(child)
# test call
print("-" * 20)
print([o.name for o in walk_children(context.object) if o.type == 'MESH'])
Output.
--------------------
['Sphere', 'Cube', 'Cube.002', 'Cube.001', 'Cube.003']
A dictionary by type
One of my favourite things is the defaultdict type from collections module.
from collections import defaultdict
descendants = defaultdict(list)
for o in walk_children(context.object):
descendants[o.type].append(o)
for k, obs in descendants.items():
print(k, [o.name for o in obs])
Output:
EMPTY ['Empty']
ARMATURE ['Armature']
MESH ['Sphere', 'Cube', 'Cube.002', 'Cube.001', 'Cube.003']
CAMERA ['Camera', 'Camera.001']
With levels and yield only of a type
import bpy
context = bpy.context
def walk_children(ob, level=0, max_level=50, type='MESH'):
print(f"{' ' * level}{ob.name}")
if ob.type == type:
yield ob
if level < max_level:
for child in ob.children:
yield from walk_children(child, level=level + 1)
# test call
print("-" * 20)
print([o.name for o in walk_children(context.object)])
Output.
--------------------
Empty
Armature
Sphere
Camera
Cube
Cube.002
Camera.001
Cube.001
Cube.003
Lamp
['Sphere', 'Cube', 'Cube.002', 'Cube.001', 'Cube.003']
A "wrapper"
As also demonstrated in the upvoteworthy answer of @lemon
If we wrap our recursive generator above in a method, can pass initial arguments, and manipulate the result.
Example below uses the absolute scene depth of the root object passed to find the names of all objects at a particular depth.
def walk_children(ob, min_level=0, max_level=100):
def get_level(ob):
i = 0
while(ob.parent):
i += 1
ob = ob.parent
return i
def _walk_children(ob, level=get_level(ob)):
yield level, ob.name
for child in ob.children:
yield from _walk_children(child, level=level + 1)
#
return list(o for lev, o in _walk_children(ob)
if min_level <= lev <= max_level)
# test call
root_obs = [o for o in context.scene.objects
if o.parent is None]
for root in root_obs:
print(root.name, walk_children(root, min_level=2, max_level=2))
Output, name of all objects in scene with a depth two ancestors.
Empty ['Sphere', 'Cube.002', 'Cube.003', 'Lamp']