t Book t Book - 11 months ago 95
Python Question

python create html table from dict

I´m just starting learning python and therefore like to create a html table based on filenames.
Imaging following files


The kind of object is always the first part (before the first dot) the quality property is somewhere in the name.

My resulting table should look like this:

Object Name | good | bad | ugly
apple | apple.good.1.svg |
| apple.good.2.svg |
banana | | banana.bad.2.svg | banana.1.ugly.svg
kiwi | kiwi.good.svg

This is what I did so far

import glob
from collections import defaultdict

fileNames = defaultdict(list)

# fill sorted list of tables based on svg filenames
svgFiles = sorted(glob.glob('*.svg'))
for s in svgFiles:
fileNames[s.split('.', 1)[0]].append(s)

# write to html
html = '<html><table border="1"><tr><th>A</th><th>' + '</th><th>'.join(dict(fileNames).keys()) + '</th></tr>'

for row in zip(*dict(fileNames).values()):
html += '<tr><td>Object Name</td><td>' + '</td><td>'.join(row) + '</td></tr>'

html += '</table></html>'

file_ = open('result.html', 'w')

I managed to read the files sorted in a dict:

{'kiwi': ['kiwi.good.svg'], 'apple': ['apple.good.2.svg', 'apple.good.1.svg'], 'banana': ['banana.1.ugly.svg', 'banana.bad.2.svg']}

But fail by generating the html table.

enter image description here

How can I build the html table as shown above? Where Objects are written to the first first column of a row and the filename in columns depending on their quality property?

Answer Source

You have to iterate all combinations of fruits from your dictionaries and states, and then create one line (instead of one column) for each fruit. Then just iterate all the files matching that fruit and filter those that contain the current state and join those in one cell.

d = {'kiwi': ['kiwi.good.svg'], 'apple': ['apple.good.2.svg', 'apple.good.1.svg'], 'banana': ['banana.1.ugly.svg', 'banana.bad.2.svg']}

html = """<html><table border="1">
for fruit in d:
    html += "<tr><td>{}</td>".format(fruit)
    for state in "good", "bad", "ugly":
        html += "<td>{}</td>".format('<br>'.join(f for f in d[fruit] if ".{}.".format(state) in f))
    html += "</tr>"
html += "</table></html>"


enter image description here

Update: If you have state expressions that are part of other states, like bad and medium_bad, then just using in won't work. Instead, you can use a regular expression to get the best match.

>>> fruit = "banana_bad.svg", "banana_medium_bad.svg"
>>> [re.search(r"[._](good|bad|medium_bad|ugly)[._]", f).group(1) for f in fruit]
['bad', 'medium_bad']

You could use this code then:

d = {'kiwi': ['kiwi.good.svg', 'kiwi_medium_bad.svg'], 'apple': ['apple.good.2.svg', 'apple.good.1.svg'], 'banana': ['banana.1.ugly.svg', 'banana.bad.2.svg']}
states = ['good', 'bad', 'medium_bad', 'ugly']

html = """<html><table border="1">
for fruit in d:
    html += "<tr><td>{}</td>".format(fruit)
    by_state = {f: re.search(r"[._]({})[._]".format('|'.join(states)), f).group(1) for f in d[fruit]}
    for state in states:
        html += "<td>{}</td>".format('<br>'.join(f for f in d[fruit] if by_state[f] == state))
    html += "</tr>"
html += "</table></html>"

Alternatively, you could also restructure your dictionary to have another "layer" of states, i.e. {"kiwi": {"good": ["kiwi.goog.svg"]}, ...}