-1

Example input:

[('b', 'c', 4),
('l', 'r', 5),
('i', 'a', 6),
('c', 't', 7),
('a', '$', 8),
('n', '$', 9)]

[0] contains the vertical heading, [1] contains the horizontal heading.

Example output:

  c r a t $ $
b 4  
l   5
i     6
c       7
a         8
n           9

Note: given enough tuples the entire table could be filled :P

How do I format output as a table in Python using [preferably] one line of code?

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
A T
  • 13,008
  • 21
  • 97
  • 158
  • What do you mean by "format as table"? Can you provide an example of the kind of formatting you'd like? – Li-aung Yip May 18 '12 at 04:28
  • I've included the example output in the tabular output I am looking for. – A T May 18 '12 at 04:29
  • 1
    It is not clear from the example exactly what you are looking for. What do the dots represent? What pattern is followed in the table? – Josiah May 18 '12 at 04:38
  • Header row, left column row. Add the number together to the corresponding one (by cell position). Show the results in a table structured like above. – A T May 18 '12 at 04:50
  • Thank you, that's more clear. – Josiah May 18 '12 at 04:51
  • 1
    Your example input doesn't compile for me (Python 2.7) - you're missing at least a colon from the end of your `def`, amongst other errors. – Li-aung Yip May 18 '12 at 05:16
  • Apologies, I have fixed the example to be much clearer. – A T May 18 '12 at 14:07
  • Your edited question is much more challenging than your original question. ;) Is the data always constrained to be single characters, or can it be variable width? (That is, can the strings be two or more characters? Can the numbers be more than one digit wide? Can they be negative? Can they be floats?) Also, should there only be one `$` column in your example above? – Li-aung Yip May 20 '12 at 04:35
  • Nope, the number of dollar signs are correct. Each entry in the tuple is guarnateed to be one char length (either str or int). – A T May 20 '12 at 05:17
  • What's the point of having a `$` column if it's not going to contain all of the entries for `$`? Should `8` and `9` both be under the same column? – Li-aung Yip May 20 '12 at 10:20
  • Nope, this is exactly as it should be. Words can have multiple repeating characters. Although admittedly this might make filling up the entire table... impossible – A T May 20 '12 at 10:37
  • @AT: See revised answer below. – Li-aung Yip May 20 '12 at 11:55
  • Thanks, had to make a slight change but otherwise good. Is the zip call computationally expensive, and if so, can it be skipped whilst still keeping it within 1-2 lines? – A T May 20 '12 at 12:58
  • The zip call is just a shortcut for making a list of "first elements of the lists", "second elements of the list", and so on. It does materialise the entire list at once, but it shouldn't be too expensive. If in doubt (and if the time *actually matters*), benchmark and test. (Possible improvement: use `itertools.izip` instead of `zip`.) – Li-aung Yip May 20 '12 at 13:15
  • Thanks, very clear explanation. I'm sure I'll be fine with zip, just wanted to know the bounds + purpose :) – A T May 20 '12 at 13:58

2 Answers2

4

Here's an answer for your revised question:

data = [
    ['A','a','1'],
    ['B','b','2'],
    ['C','c','3'],
    ['D','d','4']
]

# Desired output:
#
#   A B C D
# a 1
# b   2
# c     3
# d       4

# Check data consists of colname, rowname, value triples
assert all([3 == len(row) for row in data])
# Convert all data to strings
data = [ [str(c) for c in r] for r in data]
# Check all data is one character wide
assert all([1 == len(s) for s in r for r in data])

#============================================================================
# Verbose version
#============================================================================
col_names, row_names, values = zip(*data) # Transpose

header_line = '  ' + ' '.join(col_names)
row_lines = []
for idx, (row_name, value) in enumerate(zip(row_names,values)):
    # Use '  '*n to get 2n consecutive spaces.
    row_line = row_name + ' ' + '  '*idx + value
    row_lines.append(row_line)

print header_line
for r in row_lines:
    print (r)

Or, if that's too long for you, try this:

cs, rs, vs = zip(*data)
print ('\n'.join(['  '+' '.join(cs)] + [r+' '+'  '*i+v for i,(r,v) in enumerate(zip(rs,vs))]))

Both have the following output:

  A B C D
a 1
b   2
c     3
d       4

Here's the kernel of what you want (no reader row or header column)

>>> print('\n'.join([ ''.join([str(i+j+2).rjust(3)
    for i in range(10)]) for j in range(10) ]))

  2  3  4  5  6  7  8  9 10 11
  3  4  5  6  7  8  9 10 11 12
  4  5  6  7  8  9 10 11 12 13
  5  6  7  8  9 10 11 12 13 14
  6  7  8  9 10 11 12 13 14 15
  7  8  9 10 11 12 13 14 15 16
  8  9 10 11 12 13 14 15 16 17
  9 10 11 12 13 14 15 16 17 18
 10 11 12 13 14 15 16 17 18 19
 11 12 13 14 15 16 17 18 19 20

It uses a nested list comprehension over i and j to generate the numbers i+j, then str.rjust() to pad all fields to three characters in length, and finally some str.join()s to put all the substrings together.

Li-aung Yip
  • 12,320
  • 5
  • 34
  • 49
1

Assuming python 2.x, it's a bit ugly, but it's functional:

import operator
from functools import partial
x = range(1,11)
y = range(0,11)
multtable = [y]+[[i]+map(partial(operator.add,i),y[1:]) for i in x]
for i in multtable:
    for j in i:
        print str(j).rjust(3),
    print

  0   1   2   3   4   5   6   7   8   9  10
  1   2   3   4   5   6   7   8   9  10  11
  2   3   4   5   6   7   8   9  10  11  12
  3   4   5   6   7   8   9  10  11  12  13
  4   5   6   7   8   9  10  11  12  13  14
  5   6   7   8   9  10  11  12  13  14  15
  6   7   8   9  10  11  12  13  14  15  16
  7   8   9  10  11  12  13  14  15  16  17
  8   9  10  11  12  13  14  15  16  17  18
  9  10  11  12  13  14  15  16  17  18  19
 10  11  12  13  14  15  16  17  18  19  20

Your problem is so darn specific, it's difficult to make a real generic example.

The important part here, though, is the part that makes the table, rathter than the actual printing:

[map(partial(operator.add,i),y[1:]) for i in x]
Josiah
  • 3,266
  • 24
  • 24
  • Sorry, forgot to mention that I am needing it for strings as well. Numbers as the results, e.g. comparing gödel numbering of characters. Would this still work for that? – A T May 18 '12 at 05:22
  • I believe `(str(j)+"".join([' ' for _ in range(2-len(str(j)))]))` can also be said `str.ljust(3)`. – Li-aung Yip May 18 '12 at 05:25
  • Indeed it can, I was just in the process of changing it to rjust, which I wasn't actually aware of, so thank you for teaching me that. – Josiah May 18 '12 at 05:26
  • @AT: I believe we've both read your question as "how do I format the *specific input* shown as a table?" If you want to ask a general question, you need to indicate that a general solution is required. – Li-aung Yip May 18 '12 at 05:26
  • @A T It will work on any data type operator.add will have the desired effect on. So no, probably not on strings. – Josiah May 18 '12 at 05:28
  • @Josiah: the multiplication operator on string is also defined as `'s'*n = 's'+'s'+'s'+...` so you could also have said it something like `'a'*3 -> 'aaa'`. `ljust()` and `rjust()` are the right tools here, though. – Li-aung Yip May 18 '12 at 05:28
  • @Josiah: `operator.add` certainly is defined for strings. `'a' + 'b' -> 'ab'`. – Li-aung Yip May 18 '12 at 05:29
  • I know it's defined for strings, but it does not work in the way it appears he is saying he wants it to. – Josiah May 18 '12 at 05:29
  • @AT, You can replace operator.add with any function you want though, so if you write a function that will "add" two strings correctly in the way you want, you can replace the operator.add with a reference to your function. – Josiah May 18 '12 at 05:33
  • @Josiah: This conjecture is all well and good, but the OP needs to clarify exactly what he wants - preferably using **real** data, not a toy example using numbers. – Li-aung Yip May 18 '12 at 05:42
  • Apologies, I have fixed my example. – A T May 18 '12 at 14:08