0

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)

Example

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.

Desired Result

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 
                               

Edit: Using list as a variable, I keep shooting myself in the foot with that one

Community
  • 1
  • 1

2 Answers2

1

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.

corn3lius
  • 4,857
  • 2
  • 31
  • 36
  • This is exactly what I wanted to do. It's actually going to end up being pasted into a cell in an excel sheet. But, I can always modify the print statements. – ProfessionalNameForDisplayHere Jun 15 '17 at 18:05
  • doesn't excel wrap the strings for you ?? – corn3lius Jun 15 '17 at 18:06
  • Yes. I didn't give the whole story in my original question. These are software requirements that will be uploaded to a database, the excel part is an inbetween step to getting them uploaded to the database. At the end of the day, the data will end up displayed somewhere, and the way it is now, it will just dump a huge ball of text with no formatting in there. So, I'm trying to format it somewhat so the tester can actually see what the table format used to be. – ProfessionalNameForDisplayHere Jun 15 '17 at 18:13
  • This is the correct answer in the bounds of my question. And I was able to modify it to do what I wanted it too. As for why I didn't use a csv file for this. I'm stuck with the upload tools I was given, that work with excel. – ProfessionalNameForDisplayHere Jun 15 '17 at 20:44
  • You're original answer works for most data. But, there are some instances where long strings will cause your while loop to enter an infinite loop. I'm not sure what causes it. I will edit my original post for this when I figure out what is causing it. – ProfessionalNameForDisplayHere Jun 15 '17 at 21:29
  • probably while `row != ["","",""]` this was only written for rows with three colomns – corn3lius Jun 15 '17 at 21:34
  • Surprisingly I haven't changed the number of expected columns. It shows up when one of the strings is excessively long. I put the pdb debugger in there and stepped through it, and for some reason it works up until it gets to a certain cell with a long string, and then it just takes off in an infinite loop. When I change it from column width of 30 to 45. It stopped producing an infinite loop again. – ProfessionalNameForDisplayHere Jun 15 '17 at 21:38
0

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.

zwer
  • 24,943
  • 3
  • 48
  • 66