# convert a list into an ASCII table
# (c) 2010 Peter Goodman, all rights reserved.
DEFAULT_TABLE_STYLES = ("|","-","|","-","+"," ")
DEFAULT_TABLE_JUSTIFY = str.ljust
DEFAULT_TABLE_CELL_TO_STR = str
def list_to_table(table, title="",
cell_to_str=DEFAULT_TABLE_CELL_TO_STR,
justify=DEFAULT_TABLE_JUSTIFY,
styles=DEFAULT_TABLE_STYLES):
"""Convert a list into a string that is formatableed to look like a table."""
def make_row_list(row):
if not isinstance(row, list):
return [row]
return row
COL_SEP, ROW_SEP, VERT_BORDER, HOR_BORDER, INTERSECT, PADDING = styles
table = map(make_row_list, table)
col_range, row_range = xrange(len(zip(*table))), xrange(len(table))
col_max_widths = dict.fromkeys(col_range, 0)
row_max_heights = dict.fromkeys(row_range, 1)
cells = { }
# get string representations for each cell
for r in row_range:
for c in col_range:
cells[r, c] = cell_to_str(table[r][c]).splitlines()
# get max row heights and col lengths
for ((r, c), lines) in cells.items():
row_max_heights[r] = max(row_max_heights[r], len(lines))
for line in lines:
col_max_widths[c] = max(col_max_widths[c], len(line))
# add in extra lines to the cells and justify the cells
for ((r, c), lines) in cells.items():
empty = " " * col_max_widths[c]
for i in xrange(len(lines)):
lines[i] = justify(lines[i], col_max_widths[c])
for _ in xrange(len(lines), row_max_heights[r]):
lines.append(empty)
# make the horizontal line separator
line_sep = ""
if len(ROW_SEP) or len(INTERSECT):
line_sep += VERT_BORDER + ROW_SEP
for c in col_range:
if c > 0:
line_sep += (INTERSECT * len(COL_SEP)) + ROW_SEP
line_sep += ROW_SEP + (col_max_widths[c] * ROW_SEP)
line_sep += VERT_BORDER
line_sep += "\n"
ret = ""
# construct the table
for r in row_range:
if r > 0:
ret += "\n" + line_sep
for l in xrange(row_max_heights[r]):
if l > 0:
ret += PADDING + VERT_BORDER + "\n"
for c in col_range:
if c > 0:
ret += PADDING + COL_SEP + PADDING
else:
ret += VERT_BORDER + PADDING
ret += cells[r, c][l]
ret += " " + VERT_BORDER
header, footer, sep_len = "", "", len(line_sep) - 1
# make the table header and footer
if len(HOR_BORDER):
footer = INTERSECT + (HOR_BORDER * (sep_len - 2 * len(INTERSECT))) + INTERSECT
if len(title):
header += INTERSECT + (HOR_BORDER * (2 - len(INTERSECT)))
header += " " + title + " "
header += HOR_BORDER * (sep_len - len(header) - len(INTERSECT))
header += INTERSECT + "\n"
header += VERT_BORDER + ((sep_len - 2 * len(VERT_BORDER)) * " ")
header += VERT_BORDER
else:
header = footer
header += "\n"
footer += "\n"
return header + ret + "\n" + footer
def cell_to_str_with_subtables(
cell_to_str=DEFAULT_TABLE_CELL_TO_STR,
justify=DEFAULT_TABLE_JUSTIFY,
styles=DEFAULT_TABLE_STYLES):
"""Returns a function to convert a cell to a string. If a cell is a list then it
treats the cell as a table an prints it as such."""
def to_str(cell):
if isinstance(cell, list):
return list_to_table(
cell,
cell_to_str=cell_to_str_with_subtables(cell_to_str),
justify=justify,
styles=(styles[0], styles[1], "", "", styles[4], styles[5])
)
return cell_to_str(cell)
return to_str
print list_to_table(
[
["a","b","c"],
[1,[2,3,4],5],
[[[6,7],[8,9]],10,"11\n12\n13"]
],
title="Hello World",
cell_to_str=cell_to_str_with_subtables(justify=str.rjust)
)