Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Printing evenly spaced table from a list with a for-loop

I'm sorry for asking this really elementary question, but I'm really stuck here... I've tried googling this, and using the search function, but can't find what I'm looking for.

I'm trying to print a table of values from a list of objects. But as the "name" string varies a lot in size, it skews the whole table, making it almost unreadable. I've tried adding tabs inbetween with \t, but the columns are still disaligned. Is there something I can add to this print statement to make a nice, straight table?

print "name","level","value"
for i in self.items:
    print i.name, i.lvl, i.value
like image 829
zxz Avatar asked Dec 03 '25 11:12

zxz


1 Answers

You can try something like this:

In [1]: headers = ["name","level","value"]

In [2]: vals1 = ["Some long name", "a level", "a value"]

In [3]: vals2 = ["An even longer name", "another level", "another value"]

In [4]: max_lens = [len(str(max(i, key=lambda x: len(str(x))))) for i in zip(headers, vals1, vals2)]

In [5]: for row in (headers, vals1, vals2):
   ...:     print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip(row, max_lens))
   ...:
   ...:
name               |level        |value
Some long name     |a level      |a value
An even longer name|another level|another value

This finds the maximum length of the rows in your data and prints a table that is evenly spaced. In this case, max_lens uses zip to zip together all items in a given 'column' (think all items in the name column, for instance). Then, it finds the length of the longest string (as @Bakuriu points out, these need to be converted to strings in case any of the fields aren't strings) and stores that as the length of the 'column'. Then in the iteration, you specify a width which is going to be equal to the maximum length of that 'column', and pass in the value for that column on that row (hope that makes sense :) ).

The format method makes use of a the very powerful string formatting specification. This is a pretty basic example, but it can be modified to fit much more dynamic situations (such as those where you have numerous rows, etc.).

As for an example of how it could work with your data, you could try the below example. Note that this is not the most readable of code (which is important in Python), so if you were to do something similar, it may be worth it to actually write out some of the for loops so that it is a bit more obvious what is going on (since as I said, it is a bit obfuscated :) ):

In [1]: class MyClass(object):
   ...:     def __init__(self, a, b, c):
   ...:         self.name = a
   ...:         self.level = b
   ...:         self.value = c
   ...:
   ...:

In [2]: headers = ['name', 'level', 'value']

In [3]: vals1 = MyClass('Some long name', 'a level', 10348)

In [4]: vals2 = MyClass('An even longer name', 'another level', 100008484)

In [5]: items = (vals1, vals2)

In [6]: item_lens = [[getattr(item, x) for x in headers] for item in items]

In [7]: max_lens = [len(str(max(i, key=lambda x: len(str(x))))) for i in zip(*[headers] + item_lens)]

In [8]: print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip(headers, max_lens))
name               |level        |value

In [9]: for i in items:
   ...:     print '|'.join('{0:{width}}'.format(x, width=y) for x, y in zip([getattr(i, x) for x in headers], max_lens))
   ...:
   ...:
Some long name     |a level      |10348
An even longer name|another level|100008484
like image 104
RocketDonkey Avatar answered Dec 06 '25 02:12

RocketDonkey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!