Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I wrote this module in python for some minor tabulation needs in my science projects. This was very helpful for testing, debugging and plotting various data. I often use dictionaries for storing such data, either as an array or as in a spreadsheet (column and row). This tiny script helps me to plot such data in a similar fashion. Since I find myself using it a lot, I thought of improving it in terms of performance and readability. Also, I do understand that there are several third party libraries available for python to do the same with much more functionality, but I did it anyway for learning the language and ... well, and for fun. How could I refactor this code so that I could optimize it and possibly expand it in the future?

Summary

  • For dictionaries only
  • Rows can be specified separately for excluding some data
  • Automatic column spacing
  • Automatic table breaking (terminal size)
  • Specify headers and alignment

Dictionaries represent columns. Their keys marks the rows and their values contains the cell data. This produces the structure cell=column[row] which is very much similar to the A1 structure in spreadsheet programs.

I have added doc strings and comments to make this somewhat readable, but I would very much appreciate it if you could suggest an improvement. The Table class could be instantiated to specify rows and other features. User could then add columns using AddColumn method and that's pretty much it. Use Show to view the final table.

The [[]] in the code are used for containing info about each breaks in the table. [[break1], [break2], [break3] ...]. Each of these breaks contains columns that could fit inside the terminal window. [[c1,c2,c3...c7], [c8,c9], [c10,c11...] ...].

from shutil import get_terminal_size as termsize


class Table(object):
    """Lightweight tabulation class for dictionary type containers.

    Dictionaries represents columns of the table, and their values the cells.
    Keys identifies the rows of the table.
    """

    def __init__(self, rows, header="",align="<"):
        """Initialize the table by setting rows.

        'rows' must be an iterable containing the keys for every column. Each
        cell in the table is represented as : cell = column[row].
        Header for the row can be specified through 'head'.
        """
        # Row info
        self.rows = rows
        self.rowlength = self.GetMaxLength(rows, [header]) + 2
        self.rowstyle = "".join(("{:", align, str(self.rowlength), "}"))
        self.rowhead = self.rowstyle.format(str(header))

        # Table info (list of lists)
        self.columns = [[]]
        self.styles = [[]]
        self.headers = [[]]

        self.linelength = self.rowlength
        self.linelimit = termsize().columns


    def GetMaxLength(self, *iterables, threshold=0):
        """Returns the length of the longest item among the iterables.

        'threshold', if specified, must be the cut-off value of length.
        """
        for item in iterables:
            length = max(len(str(i)) for i in item)
            if length > threshold:
                threshold = length
        return threshold


    def AddColumn(self, content, header="", align="<"):
        """Adds a column to the table.

        'content' must be a dictionary containing the column values.
        Header for the column can be specified using 'header' argument.
        """
        length = self.GetMaxLength(content.values(), [header]) + 2
        style = "".join(("{:", align, str(length), "}"))

        self.linelength += length
        if self.linelength > self.linelimit:
            # Breaks the table
            self.columns.append([content])
            self.styles.append([style])
            self.headers.append([style.format(str(header))])
            self.linelength = self.rowlength + length
        else:
            # Resumes without breaking the table
            self.columns[-1].append(content)
            self.styles[-1].append(style)
            self.headers[-1].append(style.format(str(header)))


    def Show(self):
        """Displays the table."""
        for cols, stls, heds in zip(self.columns, self.styles, self.headers):
            # Print headers
            print("\n\x1b[7;1m", self.rowhead, "".join(heds), "\x1b[0m", sep="")
            # Print cells
            for row in self.rows:
                line = [s.format(str(c[row])) for s, c in zip(stls,cols)]
                print("\x1b[1m", self.rowstyle.format(row), "\x1b[0m",
                      "".join(line), sep="")


def test():
    # Sample data
    from random import sample
    rows = sample(range(100,200), 10)
    data = {k:v for k,v in zip(rows, sample(Table.__doc__.split(),10))}

    # Tabulate them
    table = Table(rows, header="INDEX")
    for i in range(3):
        table.AddColumn(data, header="LEFT ALIGN")
    for i in range(3):
        table.AddColumn(data, header="CENTRE ALIGN", align="^")
    for i in range(3):
        table.AddColumn(data, header="RIGHT ALIGN", align=">")
    table.Show()


if __name__ == '__main__':
    test()
share|improve this question

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.