TransWikia.com

Print a right-justified list of a list

Code Review Asked by Phillip C on October 27, 2021

I’m working through Chapter 6 in "Automate the Boring Stuff, 2nd Edition." Here is the chapter project at the end.

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

The first time I tackled this project, here was what I came up with:

#! python3
# Displays a table with each column right-justified.

table_data = [["apples", "oranges", "cherries", "bananas"],
    ["Alice", "Bob", "Carol", "David"],
    ["dogs", "cats", "moose", "goose"]]

def print_table(table_data):
    # Create a new list of 3 "0" values: one for each list in table_data
    col_widths = [0] * len(table_data)
    # Search for the longest string in each list of table_data
    # and put the numbers of characters in the new list
    for y in range(len(table_data)):
        for x in table_data[y]:
            if col_widths[y] < len(x):
                col_widths[y] = len(x)

    # Rotate and print the list of lists
    for x in range(len(table_data[0])):
        for y in range(len(table_data)):
            print(table_data[y][x].rjust(col_widths[y]), end=" ")
        print()

print_table(table_data)

The second time, I found out about the MAX method after several searches on Google. Here is what I came up with using the MAX method.

#! python3
# Displays a table with each column right-justified

table_data = [["apples", "oranges", "cherries", "bananas"],
            ["Alice", "Bob", "Carol", "David"],
            ["dogs", "cats", "moose", "goose"]]

def print_table(table_data):
    # Create a new list of 3 "0" values: one for each list in table_data
    col_widths = [0] * len(table_data)
    # Search for longest string in each list of table_data
    # and put the numbers of characters in new list
    for y in range(len(table_data)):
        x = max(table_data[y], key=len)
        if col_widths[y] < len(x):
            col_widths[y] = len(x)
    
    # Rotate and print the lists of lists
    for x in range(len(table_data[0])):
        for y in range(len(table_data)):
            print(table_data[y][x].rjust(col_widths[y]), end=" ")
        print()

print_table(table_data)

Any feedback would be greatly appreciated. Thanks!

2 Answers

First, get the width of each column. This can be easily done with a list comprehension:

    width = [max(len(item) for item in column) for column in tableData]

Where max(len(item) for item in column) gets the maximum length of an item in the column. And the [ ... for column in tableData] repeats it for each column.

Then build a format string to format each row in the desired format.

    fmt = ' '.join(f"{{:>{w}}}" for w in width)

Where f"{{:>{w}}}" is a f-string expression. Double braces get replaced by single braces and {w} gets replace by the value of w. For example, with w = 8 the f-string evaluates to "{:>8}". This corresponds to a format to right justify (the >) in a field of width 8. A separate string is created for each column, which are joined by a space (' '). In the example table, width = [8, 5, 5], so fmt is "{:>8} {:>5} {:>5}".

Lastly, print each row of the table using the format.

    for row in zip(*tableData):
        print(fmt.format(*row))

Where for row in zip(*tableData) is a Python idiom for iterating over the rows when you have a list of columns.

Putting it together:

def print_table(table_data):
    width = [max(len(item) for item in col) for col in tableData]

    fmt = ' '.join(f"{{:>{w}}}" for w in width)

    for row in zip(*tableData):
        print(fmt.format(*row))

Answered by RootTwo on October 27, 2021

You can transpose the given list of lists using a single line:

zip(*table_data)

That's it. Now, to find the longest word in each column, it would be another 1-liner:

map(len, [max(_, key=len) for _ in table_data])

You are calculating len(table_data) a lot of times. Better to store it as a variable? Although, another comprehension could be:

for row in zip(*table_data):
    for index, cell in enumerate(row):
        print(cell.rjust(col_width[index]), end=" ")

You whole function now becomes:

from typing import List
def print_table(table_data: List):
    col_width = list(map(len, [max(_, key=len) for _ in table_data]))
    for row in zip(*table_data):
        for index, cell in enumerate(row):
            print(cell.rjust(col_width[index]), end=" ")
        print()

Answered by hjpotter92 on October 27, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP