3

I currently have a nested dictionary object in Python that I want to loop through to essentially create an html table. I already know the basics on what to do, but need help with determining the number of rows each column should span. Let me explain more with an example:

Input:

{  
   "system":{  
      "System Apps":{  
         "SystemEnv":[  
           'App Test',
            'App Memory',
             'App Test']

        "SystemEnv2":{  
         "System Test":[  
           'App Test']
         }},
         "System Memory":{  
            "Memeory Test":[  
               'Memory Func',
               'Apes Test']
            }
         }
      }
   }
}

Output: Html Table Output

The problem lies in putting the rowspan attribute and having the correct number of rows to span. I understand that it is the number of children the parent has, but I can seem to figure out how to code it.

Also second priority, but if someone sees a more efficient way of doing this, please let me know.

 for level1 in dictObj:
        html += "<tr>"
        html += '<td>{}</td>'.format(level1)
        for level2 in dictObj[level1]:
            if not first_run:
                html += "<tr>"
            html += '<td>{}</td>'.format(level2)
            first_run = True
            for level3 in dictObj[level1][level2]:
                if not first_run:
                    html += "<tr>"
                html += '<td>{}</td>'.format(level3)
                first_run = True
                for app in dictObj[level1][level2][level3]:
                    if not first_run:
                        html += "<tr>"
                    first_run = True
                    for test in dictObj[level1][level2][level3][app]:
                        if not first_run:
                            html += "<tr>"
                        html += '<td>{}</td>'.format(test)
                        html += '<td>{}</td>'.format(app)
                        html += '<td>{}</td>'.format('mb')
                        html += '<td>{}</td>'.format(1)
                        html += '</tr>'
                        first_run = False
2
  • Your input data does not match your desired output. Please reformat your input to mirror the table structure. Commented Jul 23, 2019 at 19:43
  • @Ajax1234 Fixed. Sorry about that mistake. Commented Jul 23, 2019 at 21:27

3 Answers 3

4

The data you provided seems incomplete, so key [System][System Apps][SystemEnv2][System Test][App Test] sticks out (its longest, every other key's shorter by 1):

data = {
   "system":{
      "System Apps":{
         "SystemEnv":[
           'App Test',
            'App Memory',
             'App Test'],
        "SystemEnv2":{
         "System Test":[
           'App Test']
         }
         },
         "System Memory":{
            "Memeory Test":[
               'Memory Func',
               'Apes Test']
            }
         }
      }
#    }
# }

def num_items(d):
    if isinstance(d, list):
        for i in d:
            for ii in num_items(i):
                yield ii
    elif isinstance(d, dict):
        for k, v in d.items():
            for ii in num_items(v):
                yield ii
    else:
        yield 1

def traverse(d, cur=[]):
    if isinstance(d, list):
        for i in d:
            cur.append( (i, sum(num_items(i))) )
            for ii in traverse(i, cur):
                yield ii
    elif isinstance(d, dict):
        for k, v in d.items():
            cur.append( (k, sum(num_items(v))) )
            for ii in traverse(v, cur):
                yield ii
    else:
        yield cur
        del cur[:]

print('<table border=4>')
for row in traverse(data):
    print('<tr>')
    for td, rowspan in row:
        print('<td rowspan={}>{}</td>'.format(rowspan, td))
    print('</tr>')
print('</table>')

Prints:

<table border=4>
<tr>
<td rowspan=6>system</td>
<td rowspan=4>System Apps</td>
<td rowspan=3>SystemEnv</td>
<td rowspan=1>App Test</td>
</tr>
<tr>
<td rowspan=1>App Memory</td>
</tr>
<tr>
<td rowspan=1>App Test</td>
</tr>
<tr>
<td rowspan=1>SystemEnv2</td>
<td rowspan=1>System Test</td>
<td rowspan=1>App Test</td>
</tr>
<tr>
<td rowspan=2>System Memory</td>
<td rowspan=2>Memeory Test</td>
<td rowspan=1>Memory Func</td>
</tr>
<tr>
<td rowspan=1>Apes Test</td>
</tr>

In browser it's like this:

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

I appreciate it @Andrej. Do you mind explaining very briefly what the groupby logic does. Also I'll try to read up on 'yield' because i'm using python 2.7 and it is complaining that I need to use a explicit iterator over a subgenerator
@rominoushana I updated my answer, simplified it and tested it with python 2.7. I've removed the groupby, now the grouping is done in the traverse() function.
1

I suggest that for each cell, with the cell name as the key, you set the row span equal to the number of items values at the further value of the dictionary in the corresponding value.

For example,

Input:

{  
   "system":{  # span = 5 since system_apps(2) + SystemEnv(1) + System_Memory(2) = 5
      "system_apps":{ # span =  2 since it only contains systemEnv with span of 2 
         "SystemEnv":{  # span = 2 since there are 2 items (test1 object, test2 object)
           test1 object,
            test2 object
         }, 
        "SystemEnv2":{  # span = 1 since it only contains system test which has span of 1 
         "System Test":{ # span = 1 (test1 object)
           test1 object
         },
         "System Memory":{  # span = 2 since it only contains memory test which contains span of 2 
            "Memory Test":{  # span = 2 (corresponds with test3 object and test4 object)
               test3 object,
               test4 object
            }
         }
      }
   }
}

If you know the level (assuming they all contain the same number of levels), set the row span to the sum of the spans of the immediate children, starting at the furthest child. Any item that is not a dictionary will automatically have a span of 1, and you just add the row spans, and then proceed to the next level until you get to the top.

1 Comment

Hi @Matthew Kligerman. Thank you very much for your response. So the problem is, I sort of had the same idea for determining the value for the rows to span, however, where I'm getting stuck at is, how would I determine the number of children the row has without looping through the deeper levels? Also if I loop through the deeper levels without writing the row to the html, then I already missed my opportunity right? Maybe some code might help me understand? Thanks!
0
        <tr>
            <td>Level1</td>
            <td>Level2</td>
            <td>Level3</td>
            <td>Info</td>
            <td>Name</td>
        </tr>
          
            
        
        <tr>
            <td rowspan="6">System</td>
            <td rowspan="4">System Apps</td>
            <td rowspan="3">SystemEnv</td>
            <td>App Test</td>
            <td>Foo</td>
        </tr>

        <tr>
            <td>App Memory</td>
            <td>Foo</td>
        </tr>

        <tr>
            <td>App Test</td>
            <td>bar</td>
        </tr>
        <tr>
            <td>SystemEnv2</td>
            <td>App Test</td>
            <td>bar</td>
        </tr>
        <tr>
            <td rowspan="2">System Memory</td>
            <td rowspan="2">Memory Test</td>
            <td>Memory func</td>
            <td>foo</td>
        </tr>
        <tr>
            <td>App Test</td>
            <td>Foo</td>
        </tr>

    </table>
</div>

1 Comment

As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.

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.