Q:

Print List of Lists in Neat Columns/Table

I have a list of lists with the structure [[s: str, s: str, s: str]] and there can be any number of items (lists) inside of it.
I want to print this cleanly in columns like so:
FirstFirst     FirstSecond    FirstThird
SecondFirst    SecondSecond   SecondThird
ThirdFirst     ThirdSecond    ThirdThird
foo            bar            foobar

So, despite having different lengths, each item in the sublists are left-justified in a column.
I have already tried very complex list comprehensions like
lists = [['FirstFirst', 'FirstSecond', 'FirstThird'],
         ['SecondFirst', 'SecondSecond', 'SecondThird'],
         ['ThirdFirst', 'ThirdSecond', 'ThirdThird'],
         ['foo', 'bar', 'foobar']]

[print(f'{_[0]}{" " * (15 - len(_[0]))}{_[1]}{" " * (30 - len(_[0] + _[1] + " " * (15 - len(_[0]))))}{_[2]}') for _ in lists]

While this does work, it is very bruteforce-ish.
What's worse is that this list comprehension is not at all scalable. If I want to add another string to each item in the sublists, I'll have to add a lot more to my list comprehension to make it all still work. Also, everything is foiled if I want two lists to have different lengths.
What is a better way of doing this?

A:

Using list comprehensions for a side effect is bad style: you create a full list (wich takes time & memory) that gets thrown away afterwards - use simple loops.
You need to calculate the length of each word (in each column). Getting the maximum length of your words is simple:
data = [['FirstFirst', 'FirstSecond', 'FirstThird'],
         ['SecondFirst', 'SecondSecond', 'SecondThird'],
         ['ThirdFirst', 'ThirdSecond', 'ThirdThird'],
         ['foo', 'verylongwordinsidehere', 'bar', ]]    # changed to a longer one

# get max word length
max_len = max(len(i) for j in data for i in j)

# do not use list comp for printing side effect - use a simple loop
for inner in data:
    for word in inner:
        print(f"{word:{max_len}}",end=" | ") # and format the length into it
    print()

to get
FirstFirst             | FirstSecond            | FirstThird             | 
SecondFirst            | SecondSecond           | SecondThird            | 
ThirdFirst             | ThirdSecond            | ThirdThird             | 
foo                    | verylongwordinsidehere | bar                    | 

This looks kinda ugly, would be better if you only got the max length per column imho:
# transpose the list, get the max of each column and store in as dict[column]=legnth
col_len = {i:max(map(len,inner)) for i,inner in enumerate(zip(*data))}

# print(col_len) # {0: 11, 1: 22, 2: 11}

# print using the column index from enumerate to lookup this columns lenght
for inner in data:
    for col,word in enumerate(inner):
        print(f"{word:{col_len[col]}}",end=" | ")
    print()

to get a column-width adjusted output:
FirstFirst  | FirstSecond            | FirstThird  | 
SecondFirst | SecondSecond           | SecondThird | 
ThirdFirst  | ThirdSecond            | ThirdThird  | 
foo         | verylongwordinsidehere | bar         | 

See

Understanding nested list comprehension to get a better understanding what max_len = max(len(i) for j in data for i in j) does
python matrix transpose and zip to see how transposing a list of list with zip() works

If you need to keep it shorter you can use ' | '.join() to print the lists:
# do not use list comp for printing side effect - use a simple loop
for inner in data:
    print( ' | '.join( (f"{word:{max_len}}" for word in inner)))

If you need to also print uneven lists, zip() won't do - you can get around that (research itertiols.zip_longest) but if you really need that, ask a new question with data for that after you tried something to do what you need it to do.

