0

I have a nested dictionary,

d={
    "A":1, 
    "depth":0, 
    "chain":[
        {
            "A1":0.7, 
            "depth":1,
            "chain":[
                {
                    "A11":0.3,
                    "depth":2,
                    "key2":{"direct":{},"cumulative":{"B":0.3}}, 
                    "chain":[]
                }, 
                {
                    "A12":0.4, 
                    "depth":2,
                    "chain":[
                        {
                            "A121":0.4, 
                            "depth":3, 
                            "key2":{"direct": {}, "cumulative":{"C":0.2, "D": 0.2}}, 
                           "chain": []
                        }]}]},
        {
            "A2":0.3,
            "depth":1,
            "chain":[
                {
                    "A11":0.3, 
                    "depth":2, 
                    "key2":{"direct":{}, "cumulative":{"D":0.3}},
                    "chain":[]
                }]}]}

I want to return a list that the first key is repeated by x times. x is the number of elements under "chain". In this case, it would return:

["A", "A", "A1", "A1", "A2", "A12"]

I have tried the following

def from_nodes(d):
    from_n=[list(d.keys())[0]]*len(d["chain"])
    for x in d["chain"]:
        if x is not None:
            from_n.extend(from_nodes(x))
            return from_n

and got the error

TypeError                                 Traceback (most recent call last)
<ipython-input-196-6233463c604b>in <module>()
----> 1 from_nodes(test2)

<ipython-input-194-5b7ca4b6db75>in from_nodes(d)
  3     for x in d["chain"]:
  4         if x is not None:
----> 5             from_n.extend(from_nodes(x))
  6         return from_n

<ipython-input-194-5b7ca4b6db75> in from_nodes(d)
  3     for x in d["chain"]:
  4         if x is not None:
----> 5             from_n.extend(from_nodes(x))
  6         return from_n
pault
  • 41,343
  • 15
  • 107
  • 149
xiaoshir
  • 215
  • 4
  • 17
  • 2
    What does your function return when all `x` in `d["chain"]` are `None`? – DYZ Jun 21 '18 at 15:31
  • Please include the full traceback of the error. – glibdud Jun 21 '18 at 15:31
  • 2
    If `d["chain"]` does not have anything to iterate, you never get into your `for` loop. Thus the function will return `None` You probably need to un-indent the `return from_n` line two times. – pault Jun 21 '18 at 15:32
  • `got the error TypeError: 'NoneType' object is not iterable` Edit your question to include the full error traceback. You're making us guess where the error is happening. – John Gordon Jun 21 '18 at 15:32
  • @DyZ if `x` are `None`, it doesn't extend the list with anything. – xiaoshir Jun 21 '18 at 15:36
  • @edge27 And returns `None`. Exactly my point. – DYZ Jun 21 '18 at 15:50
  • Your error message now shows code that is not the same as your posting. Please correct your transcription errors. Python is sensitive to indentation. – quamrana Jun 21 '18 at 16:07

1 Answers1

1

As I mentioned in the comments, your error is that the return statement is indented incorrectly. If d["chain"] is empty or None, your function will return None implicitly.

Changing your function to the following:

def from_nodes(d):
    from_n=[list(d.keys())[0]]*len(d["chain"])
    for x in d["chain"]:
        if x is not None:
            from_n.extend(from_nodes(x))
    return from_n

will fix the error and the result on my computer is:

print(from_nodes(d))
#['A', 'A', 'A1', 'A1', 'depth', 'depth']

Which doesn't match your desired output-- this is because you aren't guaranteed to get a deterministic order when you call .keys().

One way to modify your function for the desired output would be to create a list of keys to ignore:

def from_nodes(d):
    ignore_keys = {"chain", "depth", "key2"}
    from_n=[list(k for k in d.keys() if k not in ignore_keys)[0]]*len(d["chain"])
    for x in d["chain"]:
        if x is not None:
            from_n.extend(from_nodes(x))
    return from_n

print(from_nodes(d))
#['A', 'A', 'A1', 'A1', 'A12', 'A2']

However, this is just me speculating what your requirements are. You need to define the correct condition for what you mean by the "first" key.

pault
  • 41,343
  • 15
  • 107
  • 149
  • ['A', 'A', 'A1', 'A1', 'A12', 'A2'] is indeed my desired output, but the first solution you provided worked perfectly on my computer, without having a list of keys to ignore – xiaoshir Jun 28 '18 at 08:27
  • What version of python? After 3.6, dictionaries maintain insertion order. – pault Jun 28 '18 at 10:48