4
\$\begingroup\$

I'm new to Python and I came to the following excercise:

Write a function named printTable() that takes a list of lists of strings and displays it in a well-organized table with each column right-justified. Assume that all the inner lists will contain the same number of strings. For example, the value could look like this:

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]

Your printTable() function would print the following:

  apples Alice  dogs
 oranges   Bob  cats
cherries Carol moose
  banana David goose

My solution is this:

table_printer.py

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]

def printTable(tableData):
    """
    Print table neatly formatted:
    e.g:

    [['apples', 'oranges', 'cherries', 'banana'],
    ['Alice', 'Bob', 'Carol', 'David'],
    ['dogs', 'cats', 'moose', 'goose']]

    becomes:

      apples Alice  dogs
     oranges   Bob  cats
    cherries Carol moose
      banana David goose
    """
    # make list of ints to store later max len element of each list
    colWidths = [0] * len(tableData)

    # Store maxlen of each list
    i = 0
    while i < len(tableData):
        colWidths[i] = len(max(tableData[i], key=len))
        i = i + 1

    # Print formatted
    for x in range(len(tableData[0])):
        for y in range(len(colWidths)):
            print(tableData[y][x].rjust(colWidths[y]), end=' ')
        print(end='\n')

printTable(tableData)

I wonder if this is a good solution or if there is an easier/better way. It took me quite some time to come up with a solution. Still I feel its probaly not very elegant. Maybe I'm overcomplicating it because I came from C/C++ where you oftenly have to do stuff by hand.

I read that it's often not a good idea in python to write loops like in other languages with explicit indices (what I basically did here). Are there any alternatives?

\$\endgroup\$
1
  • \$\begingroup\$ You can compute the maximal length on each row by np.array([np.array(max([len(xii) for xii in xi])) for xi in tableData]) which returns [8 5 5]. These numbers will be used to format the strings. \$\endgroup\$
    – Sigur
    Nov 23, 2018 at 2:11

2 Answers 2

2
\$\begingroup\$

len(max(column, key=len)) can be written as:

max(len(item) for item in column)

Which can be used in a list comprehension to do all columns:

column_widths = [max(len(item) for item in column) for column in table_data]

When you have table_data organized by columns, the Python idiom zip(*table_data) can be used to iterate over the rows like this:

for row in zip(*table_data):
    ... do something with the row of data ... # row is a tuple

zip() is also used to iterate over multiple iterables (lists, tuples, etc) at once. It can be used to iterate over the row and the column widths at the same time to format and print the data:

for width, datum in zip(column_width, row):
    print(f"{datum:>{width}}", end=' ')
print()

All together:

def print_table(table):
    column_widths = [max(len(item) for item in column) for column in table]

    for row in zip(*table):
        
        for width, datum in zip(column_widths, row):
            print(f"{datum:>{width}}", end=' ')
            
        print()
  

An alternative would be to use the column widths to construct a format string and then use that to print the row data

def print_table(table):
    column_widths = [max(len(item) for item in column) for column in table]
    
    row_format = ' '.join(f"{{:>{w}}}" for w in column_widths)

    for row in zip(*table):
        print(row_format.format(*row))
\$\endgroup\$
1
\$\begingroup\$

Here is my proposal. It is shorter than OP's solution, specially to compute the length of each word to be used in the format procedure while printing.

In a single line, we obtain a 1D array with maximal lengths.

import numpy as np

tableData = [['apples', 'oranges', 'cherries', 'banana'],
             ['Alice', 'Bob', 'Carol', 'David'],
             ['dogs', 'cats', 'moose', 'goose']]

max_len = np.array([np.array(max([len(xii) for xii in xi])) for xi in tableData])

for col in range(len(tableData[0])):
    for i in range(len(tableData)):
        print ("{:>%d}" % max_len[i]).format(tableData[i][col]),
    print ""

Output

  apples Alice  dogs 
 oranges   Bob  cats 
cherries Carol moose 
  banana David goose
\$\endgroup\$
2
  • \$\begingroup\$ Hello. While the solution is a nice one, your answer just provides and alternative to OP's code which is not what this site is about. Please expand your answer and explain how you're improving OP's solution. \$\endgroup\$ Nov 24, 2018 at 12:48
  • \$\begingroup\$ @яүυк, hello. Since my knowledge is not big, I tried to improve it. To be true, my code is shorter but I am not sure if it is more efficient. What do you think? \$\endgroup\$
    – Sigur
    Nov 24, 2018 at 13:37

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.