RosettaCodeData/Task/Align-columns/Python/align-columns-5.py

76 lines
2.2 KiB
Python

'''Variously aligned columns
from delimited text.
'''
from functools import reduce
from itertools import repeat
# TEST ----------------------------------------------------
# main :: IO ()
def main():
'''Test of three alignments.'''
txt = '''Given$a$text$file$of$many$lines,$where$fields$within$a$line$
are$delineated$by$a$single$'dollar'$character,$write$a$program
that$aligns$each$column$of$fields$by$ensuring$that$words$in$each$
column$are$separated$by$at$least$one$space.
Further,$allow$for$each$word$in$a$column$to$be$either$left$
justified,$right$justified,$or$center$justified$within$its$column.'''
rows = [x.split('$') for x in txt.splitlines()]
table = paddedRows(max(map(len, rows)))('')(rows)
print('\n\n'.join(map(
alignedTable(table)(' '),
[-1, 0, 1] # Left, Center, Right
)))
# alignedTable :: [[String]] -> Alignment -> String -> String
def alignedTable(rows):
'''Tabulation of rows of cells, with cell alignment
specified by:
eAlign -1 = left
eAlign 0 = center
eAlign 1 = right
and separator between columns
supplied by the `sep` argument.
'''
def go(sep, eAlign):
lcr = ['ljust', 'center', 'rjust'][1 + eAlign]
# nextAlignedCol :: [[String]] -> [String] -> [[String]]
def nextAlignedCol(cols, col):
w = max(len(cell) for cell in col)
return cols + [
[getattr(s, lcr)(w, ' ') for s in col]
]
return '\n'.join([
sep.join(cells) for cells in
zip(*reduce(nextAlignedCol, zip(*rows), []))
])
return lambda sep: lambda eAlign: go(sep, eAlign)
# GENERIC -------------------------------------------------
# paddedRows :: Int -> a -> [[a]] -> [[a]]
def paddedRows(n):
'''A list of rows of even length,
in which each may be padded (but
not truncated) to length n with
appended copies of value v.'''
def go(v, xs):
def pad(x):
d = n - len(x)
return (x + list(repeat(v, d))) if 0 < d else x
return [pad(row) for row in xs]
return lambda v: lambda xs: go(v, xs) if xs else []
# MAIN ---
if __name__ == '__main__':
main()