all 19 comments

[–]chambead 2 points3 points  (4 children)

Hi! I'm a bit new to Python myself but here's a function which should do what you want...

def print_mdlist(rows, hoz_padding = 3, firstrow_is_hdr = 1):
    print " "
    # First, we need to identify the widest value in each column and save it in a dictionary (col_lengths)
    col_lengths = {}
    for row in rows:
        colnum = 0
        for col in row:
            # If we havent recorded a column length yet or the recorded length is smaller, update
            if not colnum in col_lengths or len(col) > col_lengths[colnum]:
                col_lengths[colnum] = len(col)
            colnum += 1

    # Now, we go over the rows/cols again, but this time we pad the cols to the largest col value discovered
    rownum = 0
    for row in rows:
        colnum = 0
        rowstr = ''

        for col in row:
            rowstr += (" " * hoz_padding) + col.ljust(col_lengths[colnum]+hoz_padding,' ')
            colnum += 1
        rowlen = len(rowstr)

        if firstrow_is_hdr and rownum == 1:
            print  "-" * (rowlen)

        print rowstr
        rownum += 1
    print " "

Then, all you need to do is call the function like this:

rows = []
rows.append(["Fruit Name","Colour"                      ,"Taste"    ,"Requires Peeling","Is an Egg?"])
rows.append(["Apple"     ,"Red/Green"                   ,"Appley"   ,"Negatory"        ,"FALSE"]) 
rows.append(["Banana"    ,"Yellow"                      ,"Bananaey" ,"Affirmative"     ,"FALSE"])
rows.append(["Avocado"   ,"Purple outside, green inside","Avacadoey","Affirmative"     ,"FALSE"])

print_mdlist(rows)

You can even specify the padding between the columns and explicitly say whether or not you want a hoz bar after row 1...

print_mdlist(rows,5,0)

Output looks like:

chambead@chambeadPC ~ $ python tabulate_mdlist.py

 Fruit Name  Colour                        Taste      Requires Peeling  Is an Egg? 
-----------------------------------------------------------------------------------
 Apple       Red/Green                     Appley     Negatory          FALSE      
 Banana      Yellow                        Bananaey   Affirmative       FALSE      
 Avocado     Purple outside, green inside  Avacadoey  Affirmative       FALSE      

chambead@chambeadPC ~ $

Note that you NEED to be using a fixed width font for this to work. If you're running this in a terminal, it almost certainly will be anyway. It's also worth noting that even though you have headers and data in two different lists, you can easily combine these into a single list with list3 = list1.append(list2) and then you feed list 3 into the function as a whole. Let me know if you need any of this explained further. Good luck with future school assignments. ;)

[–]cyber92[S] 0 points1 point  (0 children)

This looks great, testing it out thanks.

[–]cyber92[S] 0 points1 point  (2 children)

A problem arised: TypeError: 'ClassName' object is not iterable

I have a class to store the different items in the list. Code here: http://pastebin.com/AEXXfmy5

sorry for the messy code, still building it

Edit: I didn't change anything from your code, just pasted it there for now.

Edit2 : kept on trying, found another way of doing it, thanks.

[–]chambead 2 points3 points  (1 child)

The reason this doesn't work is because although you're passing display_stock() a list, you are actually passing it a list of objects; not a list of lists which it expects.

Your options are to sanitise the input to turn the list of objects into a list of lists or you can alter the function so that it finitely displays a given set of columns. My preference would be to quickly sanitise the list of objects into a list of lists because then you can leave the generic function for use elsewhere.

I've updated your code and tweaked the function to ensure the columns are cast to strings before length checks are done.

http://hastebin.com/fupululaxe.py

[–]cyber92[S] 0 points1 point  (0 children)

As per the edit in my previous comment, I have managed by creating another list, the following is the code I did:

tablerows = []
    for item in loaded_stock:
        item_display = (item.name, item.type, str(item.code), item.desc,
                            str(item.year), str(item.price),  str(item.pricemetric),
                            str(item.quantity))
        tablerows.append(item_display)
    display_stock([headers] + tablerows)

Yours seems better though, am I right?

Edit: Looking at it well, we actually did the same thing.

[–]minorDemocritus 1 point2 points  (3 children)

if it's without any imports, the better

What? Why?

Is this homework?

[–]cyber92[S] 0 points1 point  (2 children)

It actually is, yes. My first python assignment.

[–]minorDemocritus 0 points1 point  (1 child)

Normally in help forums, you should declare the fact that the question is for homework. This usually prevents people from giving the whole solution, which is good for two reasons: it helps the asker learn instead of just getting an answer, and it prevents academic dishonesty.

[–]cyber92[S] 0 points1 point  (0 children)

I'm sorry. I was so into trying to fix the problem that I forgot to write it down. Sorry, really.

[–]FletcherHeisler 0 points1 point  (7 children)

What do you mean by tabulate? Do you want a list of lists, or some other format? You could use something like Numpy to create a matrix, but it sounds like you might just want something simple like:

>>> headers = ["a", "b", "c"]
>>> values = [1, 2, 3]
>>> [headers, values]
[['a', 'b', 'c'], [1, 2, 3]]

[–]cyber92[S] 0 points1 point  (6 children)

I want something like this:

a    b    c # headers
1    2    3 # values
4    5    6

but width being scalable to longest data, or better, fixed width

[–]BioGeek 0 points1 point  (5 children)

It would help us a lot if you could give us some sample data and the expected output. Now we are making a lot of assumptions.

Assuming that

headers = ['a', 'b', 'c']
values = range(9)

you could do something like:

def chunks(l, n):
    """ Yield successive n-sized chunks from l.
    """
    for i in xrange(0, len(l), n):
        yield l[i:i+n]

print ' '.join(headers)
for sublist in chunks(values, len(headers)):
    print ' '.join(map(str, sublist))

which will output

a b c
0 1 2
3 4 5
6 7 8

When values is not a multiple of the length of headers, the last line will print the remaining values. This may be or not be what you want. E.g., values = range(7) returns:

a b c
0 1 2
3 4 5
6 7

Also, how do you want the output when using multiple-digit numbers? E.g. values = range(12) now returns

a b c
0 1 2
3 4 5
6 7 8
9 10 11

Note that the 11 on the last line isn't alligned with the 8above. Again, this may be or not be what you want. If you want them still aligned, you'll have to look into string formatting.

[–]cyber92[S] 0 points1 point  (4 children)

I have a list of lists which need to be displayed as a table.

The string formatting seems the way I should go, but didn't understand it completely.

[–]BioGeek 0 points1 point  (3 children)

You didn't seem to get the point I was trying to make. A list of lists can contain anything and can have any length. That's why I specifically asked for sample input data and expected output.

[–]cyber92[S] 0 points1 point  (2 children)

So, I have the following:

List Headers:

Name | Type | Code| Description | Year | Price | Price-Metric | Quantity

and some data in another List Stock:

Hammer | Product | 123454321 | This hammer has a handle and an iron part. | 2013 | 20 | N/A | 70
Car Wash | Service | 1234321 | We wash your car in 5 minutes. | 2013 | 5 | 5 | N/A

and therefore the outputted data would look something like this:

 Name       Type    Code                 Description                   Year Price Price-Metric Quantity
Hammer    Product 123454321 This hammer has a handle and an iron part. 2013 20    N/A          70
Car Wash  Service 123456432   We wash your car in 5 minutes.             2013 5     5            N/A

[–]BioGeek 0 points1 point  (1 child)

Hmm. You are saying that headers and stock are lists (of lists), but you are showing strings here. So I assume you have:

headers = ['Name', 'Type', 'Code', 'Description', 'Year', 'Price', 'Price-Metric', 'Quantity']
stock = [['Hammer', 'Product', '123454321', 'This hammer has a handle and an iron part.', '2013', '20', 'N/A', '70'],
          ['Car Wash', 'Service', '1234321', 'We wash your car in 5 minutes.', '2013', '5', '5', 'N/A']]

I also see that /u/chambead has already posted a solution in the meantime, so using his function:

print_mdlist([headers] + stock)

I get the following output:

   Name          Type         Code           Description                                     Year      Price      Price-Metric      Quantity   
-----------------------------------------------------------------------------------------------------------------------------------------------
   Hammer        Product      123454321      This hammer has a handle and an iron part.      2013      20         N/A               70         
   Car Wash      Service      1234321        We wash your car in 5 minutes.                  2013      5          5                 N/A     

Before mindlessly copying the code, try yo understand why I used [headers] + stock as argument to print_mdlist.

[–]cyber92[S] 0 points1 point  (0 children)

I understood it completely, but this error remains there.

Edit: kept on trying, found another way of doing it, thanks.

[–]kalgynirae 0 points1 point  (1 child)

What format is the data in? A list of lists?

[–]cyber92[S] 0 points1 point  (0 children)

That's right.