Reputation: 51
Before I go and reinvent the wheel, I want to check that somebody hasn't already come up with something for this.
I have a list of strings that I need to print out in table format. I get the data from tables that can have somewhat long strings of data in some of the cells.
If I were to try to base my column width based on the longest string, I could end up with huge column widths.
What I'm wondering is if there is something already out there that exists to append string data to another row that still lines up with the current row (basically treat it like a cell that enforces automatic padding)
listObj = ['Pre-Condition:', 'Condition:', 'Output:',
'Button is OFF', '-', 'Speed is not on',
'Button Enabled is OFF', 'Enabled is ON',
'Speed is on', 'Button Active is ON', 'Active is OFF',
'Hold steady true north', 'Button States is HOLD',
'Button States is ACCELERATOR OVERRIDE AND Set stuff is on <Stuff here>',
'Pedal to the medal here guys']
The list above is originally a three by 5 table. So I want to print everything out in columns of three. Where I run into issues is the second to last string item in the list. There are many more like this, as this is a somewhat contrived example. Any help would be greatly appreciated. I've run into this issue before, so I wanted to ask, because I'm sure other people have had this issue as well.
Pre-Condition Condition Output Button is OFF - Speed is not on Button Enabled is OFF Active is OFF Speed is on Button States is HOLD Button states is Pedal to the med ACCELERATOR OVERRIDE here guys AND Set stuff is on
Upvotes: 0
Views: 757
Reputation: 25799
Here's one way to do it in a very flexible manner:
# a simple function to do our line-splitting per value
def split_value(value, width):
result = []
while len(value) > width: # while our string is longer than allowed
split_index = value.rfind(" ", 0, width)
if split_index == -1: # no space in our current chunk, we must do hard-break
split_index = width - 1 # set the split to our column width point
result.append(value[:split_index + 1]) # add the current slice as a sub-row
value = value[split_index + 1:] # remove the added slice from our data
if value: # there are leftovers from slicing, add them as the last piece
result.append(value)
return result
# and our main function...
def draw_table(data, columns, table_width, column_border=1):
column_data = [data[i::columns] for i in range(columns)] # split the data into columns
column_width = table_width // columns - column_border # max characters per column
column_template = ("{} " * (columns - 1)) + "{}" # a simple template for our columns
empty_value = " " * (column_width + column_border) # what to print when there's no value
rows = len(max(column_data, key=len)) # in case we have more data in some of the columns
for row in range(rows): # lets print our rows
row_data = [split_value(x[row], column_width) if len(x) > row else []
for x in column_data] # lets populate our row
subrows = len(max(row_data, key=len)) # number of subrows for the current row
for subrow in range(subrows): # lets go through each of them and print them out
print(column_template.format(*[x[subrow].ljust(column_width+column_border)
if len(x) > subrow else empty_value
for x in row_data])) # print our (split) row
It's a bit twisted but gets the job done reliably and it's not all that hard to follow if you read the comments. It should produce exactly what you asked for (tho your desired result doesn't seem to fit the data you have in your list):
listObj = ['Pre-Condition:', 'Condition:', 'Output:',
'Button is OFF', '-', 'Speed is not on',
'Button Enabled is OFF', 'Enabled is ON',
'Speed is on', 'Button Active is ON', 'Active is OFF',
'Hold steady true north', 'Button States is HOLD',
'Button States is ACCELERATOR OVERRIDE AND Set stuff is on <Stuff here>',
'Pedal to the medal here guys']
# table with three columns, two spaces between columns and of total width of 80 characters
draw_table(listObj, 3, 80, 2)
Produces:
Pre-Condition: Condition: Output: Button is OFF - Speed is not on Button Enabled is OFF Enabled is ON Speed is on Button Active is ON Active is OFF Hold steady true north Button States is HOLD Button States is Pedal to the medal here ACCELERATOR OVERRIDE guys AND Set stuff is on <Stuff here>
As a bonus, it supports uneven lists so you can do something like:
listObj = ['Pre-Condition:', 'Condition:', 'Output:',
'Button is OFF', '-', 'Speed is not on',
'Button Enabled is OFF', 'Enabled is ON',
'Speed is on', 'Button Active is ON', 'Active is OFF',
'Hold steady true north', 'Button States is HOLD',
'Button States is ACCELERATOR OVERRIDE AND Set stuff is on...',
'Pedal to the medal here guys', "One extra value to prove the flow"]
draw_table(listObj, 3, 80, 2)
Which will produce:
Pre-Condition: Condition: Output: Button is OFF - Speed is not on Button Enabled is OFF Enabled is ON Speed is on Button Active is ON Active is OFF Hold steady true north Button States is HOLD Button States is Pedal to the medal here ACCELERATOR OVERRIDE guys AND Set stuff is on... One extra value to prove the flow
Future upgrades like variable column widths shouldn't be that difficult as the row data splits are external so any size can be added.
Upvotes: 0
Reputation: 4985
This is a pretty hardcoded attempt at what your looking for. There is also very little error checking for ranges. I'll let you handle that :)
mylist = ['Pre-Condition:', 'Condition:', 'Output:',
'Button is OFF', '-', 'Speed is not on',
'Button Enabled is OFF', 'Enabled is ON',
'Speed is on', 'Button Active is ON', 'Active is OFF',
'Hold steady true north', 'Button States is HOLD',
'Button States is ACCELERATOR OVERRIDE AND Set stuff is on <Stuff here>',
'Pedal to the medal here guys']
def printCell(row, cellWidth):
while row != ["","",""]:
lineformat = ("{:"+str(cellWidth) + "} | ") * 3
cells=[]
for n, cell in enumerate(row):
p = cellWidth
if len(cell) > cellWidth :
p = cell[:cellWidth].rfind(" ")
if p == -1:
p = cellWidth
row[n] = cell[p:]
else:
row[n] = ""
cells.append(cell[:p])
print(lineformat.format(*cells))
def printColumns(alist, colCount, colWidth):
for n in range(0,len(alist)-1,colCount):
printCell(alist[n:n+colCount], colWidth)
print("-" * colWidth * colCount)
if __name__ == "__main__":
printColumns(mylist,3,30)
Output:
Pre-Condition: | Condition: | Output: |
------------------------------------------------------------------------------------------
Button is OFF | - | Speed is not on |
------------------------------------------------------------------------------------------
Button Enabled is OFF | Enabled is ON | Speed is on |
------------------------------------------------------------------------------------------
Button Active is ON | Active is OFF | Hold steady true north |
------------------------------------------------------------------------------------------
Button States is HOLD | Button States is ACCELERATOR | Pedal to the medal here guys |
| OVERRIDE AND Set stuff is on | |
| <Stuff here> | |
------------------------------------------------------------------------------------------
Edit
Why not just create a csv file which can be open directly with excel?
import csv
with open('output.csv', 'w') as f:
csvOut = csv.writer(f, delimiter=',')
for n in range(0,len(mylist)-1,3):
csvOut.writerow(mylist[n:n+3])
Side Note
It is bad form to use 'list' as a variable. This can conflict with the built-in list type and should be avoided.
Upvotes: 1