4

What I'm trying to do is to get each element's level.

I'm currently using this code:

re = {0: [b.keys()[0]]}
n = 1
for a in b:
    l = []
    for e in s[a]:
        l.append(e)
    if l != []:
        re.update({n: l})
    n += 1
print re

That is giving me the following output:

{"0": ["a"], "1": ["b"], "2": ["i"], "3": ["c", "d"], "4": ["f", "g", "h"], "5": ["e", "l"]}

What I'm doing wrong here?

8
  • Why is s a string? Commented Nov 27, 2017 at 16:16
  • Try using the array representation of a binary tree. It's much easier then...Wait! It's not a binary tree!! Commented Nov 27, 2017 at 16:17
  • You code has an error: s.keys() produces: AttributeError: 'str' object has no attribute 'keys' Commented Nov 27, 2017 at 16:19
  • The s var is actually a content that I get from a json file. See it as a json.load Commented Nov 27, 2017 at 16:20
  • 1
    The keys of a dictionary are not per se ordered, so [s.keys()[0]] can lead to non-determinstic behavior. Commented Nov 27, 2017 at 16:20

3 Answers 3

4

There are some problems here:

  1. you use [s.keys()[0]] as a way to find the "root" of the tree. Note however that dictionaries are unordered (yes in the CPython-3.6 dictionaries use insertion order, but this is seen as an implementation detail), so there is no guarantee that [s.keys()[0]] is actually the root; and
  2. you perform a single pass over the dictionary, but again since dictionaries are unordered, it is not guaranteed that the parent will be assigned already.

Finding roots

So we need to solve the two problems. The first one can be solved by looking for nodes that are no child of another element. So we can write it as:

nonroots = {c for cs in s.values() for c in cs}

these are all nodes that are children, so that means that all keys in the dictionary that are not in children, are roots:

roots = [k for k in s if k not in nonroots]

The roots are on level 0, so we can set it to:

levels[0] = roots

Roots: The Next Generations

now for each iteration, we construct the next generation, by looking up the values corresponding to the previous iteration.

next_gen = [c for k in prev_gen for c in s.get(k,())]

So now we can turn this into a loop: we keep constructing a new generation as long as the previous generation contains at least one elemen. So a complete implementation is:

nonroots = {c for cs in s.values() for c in cs}
level = [k for k in s if k not in nonroots]
generation = 0
result = {}
while level:
    result[generation] = level
    generation += 1
    level = [c for k in level for c in s.get(k,())]

at the end, result is a dictionary that maps all levels to a list of children.

Note that we better use a list instead of a dictionary, since generations start with index 0, and we know that if there is a generation i, then there is also a generation i-1 (for i > 0). So we can turn it into a list like:

nonroots = {c for cs in s.values() for c in cs}
level = [k for k in s if k not in nonroots]
result = []
while level:
    result.append(level)
    level = [c for k in level for c in s.get(k,())]

This gives us:

>>> result
[['a'], ['b'], ['c', 'd'], ['i', 'e', 'l'], ['f', 'g', 'h']]

For given root(s)

We can also use an approach where the caller provides the root(s), in which case we thus generate a tree. We can alter the existing approach, for instance:

def get_levels(tree, roots):
    result = []
    roots = list(roots)
    while roots:
        result.append(roots)
        roots = [c for k in roots for c in tree.get(k,())]
    return result

Now we can thus call it with a given list of roots. In case the roots are not the real roots, we will get an ordering for the subtree(s) under the given tree:

>>> get_levels(s, ['a'])
[['a'], ['b'], ['c', 'd'], ['i', 'e', 'l'], ['f', 'g', 'h']]
>>> get_levels(s, ['c'])
[['c'], ['i']]
>>> get_levels(s, ['i','l'])
[['i', 'l']]
>>> get_levels(s, ['i','e'])
[['i', 'e'], ['f', 'g', 'h']]
Sign up to request clarification or add additional context in comments.

Comments

2

You can use recursion:

from collections import defaultdict
listing1 = defaultdict(list)
s = {"a": ["b"], "b": ["c", "d"], "c": ["i"], "d": ["e", "l"], "e": ["f", "g", "h"], "f": [], "g": [], "h": [], "i": [], "l": []}
def get_listing(current_node, count):
    global listing1 
    listing1[count].append(current_node)
    for i in s[current_node]:
        get_listing(i, count+1)

get_listing('a', 0)
print(dict(listing1))

Output:

{0: ['a'], 1: ['b'], 2: ['c', 'd'], 3: ['i', 'e', 'l'], 4: ['f', 'g', 'h']}

Note that as pointed out in the comments by @WillemVanOnsem and @quamrana, dict.keys() will return an unsorted list, thus making {0: [s.keys()[0]]} unreliable as the root start.

2 Comments

This is giving me NameError: name 'defaultdict' is not defined
@J0ker98 apologies, forgot the import. Please see my recent edit.
0

Let's assume you have a dictionary 'd' like that.

d = {"a": ["b"], "b": ["c", "d"], "c": ["i"], "d": ["e", "l"], "e": ["f", "g", "h"], "f": [], "g": [], "h": [], "i": [], "l": []}
keys=d.keys()
keys.sort()
def getKey(l):
  for key,value in d.items():
    if l == value:
      return key

val=dict()# to store level of each node
val['a']=0
for l in keys:
  for v in d.values():
    if l in v:
     key = getKey(v)
     val[l]=int(val.get(key,0))+1
     print(l,val[l])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.