Vertically print a list of strings to STDOUT into 3 columns with column lengths as balanced as possible This was a interview question I struggled with, I am posting my solution hoping to look for more elegant/efficient solutions. 

Problem: Given a list of strings, print its element out to STDOUT one by one vertically into 3 columns, while making sure the 3 columns' length are as balanced as possible.

For example, given list = ['a','b','c','d','e','f','g','h','i','j']

The output could be either:

a e h
b f i
c g j
d


OR

a d g
b e h
c f i
    j


But not:

a e i
b f j
c g 
d h  


My approach in Python 3:

Basically I put each word into a "N by 3" 2D list horizontally and try to print them out vertically. If the length is not divisible by 3, I manually increase the length of the first 2 rows depending on the remainder. This creates quite a few edge cases for me to handle separately, such as when the length of the list is between 3 and 6 or when the length is less than or equal to 3.

def assign_words(grid, words):
    i = 0
    for row in range(len(grid)):
        for col in range(len(grid[row])):
            grid[row][col] = words[i]
            i += 1
            if i == len(words):
                return

def print_words(words):
    word_count = len(words)
    rows = word_count // 3
    extra_columns = word_count % 3

    grid = [[''] * 3 for _ in range(rows)]

    # special case
    if word_count &lt;= 3:
        grid.append(['']*3)
        assign_words(grid, words)
        for row in grid:
            print (' '.join(row))
        return

    if extra_columns == 1:
        if rows &gt; 2:
            grid[0].append('')
        elif rows == 2:
            grid[1].pop()
            grid.append(['']*3)
    elif extra_columns == 2:
        if rows &gt; 2:
            grid[0].append('')
            grid[1].append('')
        else:
            grid.append(['']*3)

    assign_words(grid, words)

    # special case
    if 3 &lt; word_count &lt; 6:
        print (grid[0][0]+ ' '+grid[0][2]+' '+grid[1][1])
        print (grid[0][1]+ ' '+grid[1][0]+' '+grid[1][2])
        return

    # print grid
    for col in range(len(grid[0])):
        for row in range(len(grid)):
            if col == len(grid[row]):
                break
            print (grid[row][col], end=' ')
        print ()
 print_words(['a','b','c','d','e','f','g'])
You should take a look into Python advanced iteration capabilities, i.e. the itertools module. You’ll find a number of recipe; amongst which is take:

import itertools


def take(n, iterable):
    "Return first n items of the iterable as a list"
    return list(itertools.islice(iterable, n))


Which will help you extract columns out of your list of words. You can divide the length of the list by 3 to know how much data to take in each column, and adjust using the modulo of the length by 3 (the modulo first columns get to pick one more word); which can be eased thanks to divmod. Lastly, you need to organize your columns into rows, which is easily done using itertools.zip_longest.

That being done, you will need to print the data. The simplest solution is to only print them, as you do:

def print_words(words):
    columns, dangling = divmod(len(words), 3)
    iterator = iter(words)
    columns = [take(columns + (dangling &gt; i), iterator) for i in range(3)]
    for row in itertools.zip_longest(*columns, fillvalue=''):
        print('{}  {}  {}'.format(*row))


Which behaves like so:

&gt;&gt;&gt; print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
a  e  h
b  ffffffff  i
cccccc  gg  j
dd




Which is not pretty. So you might want to save the intermediate result to check, for each column, the length of the longest word, and apply some padding:

def print_words(words):
    columns, dangling = divmod(len(words), 3)
    iterator = iter(words)
    columns = [take(columns + (dangling &gt; i), iterator) for i in range(3)]
    paddings = [max(map(len, column)) for column in columns]
    for row in itertools.zip_longest(*columns, fillvalue=''):
        print('  '.join(word.rjust(pad) for word, pad in zip(row, paddings)))


Which behave more nicely:

&gt;&gt;&gt; print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
      a         e  h
      b  ffffffff  i
ccccccc        gg  j
     dd             




Lastly, we can generalize to support an arbitrary number of columns:

def print_words(words, column_count=3):
    columns, dangling = divmod(len(words), column_count)
    iterator = iter(words)
    columns = [take(columns + (dangling &gt; i), iterator) for i in range(column_count)]
    paddings = [max(map(len, column)) for column in columns]
    for row in itertools.zip_longest(*columns, fillvalue=''):
        print('  '.join(word.rjust(pad) for word, pad in zip(row, paddings)))


Which allows

&gt;&gt;&gt; print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'])
      a         e  h
      b  ffffffff  i
ccccccc        gg  j
     dd             
&gt;&gt;&gt; print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'], 4)
      a        dd  gg  i
      b         e   h  j
ccccccc  ffffffff       
&gt;&gt;&gt; print_words(['a', 'b', 'ccccccc', 'dd', 'e', 'ffffffff', 'gg', 'h', 'i', 'j'], 5)
a  ccccccc         e  gg  i
b       dd  ffffffff   h  j