Generate Markdown tables?
Asked Answered
I

3

7

Is there any way to generate tables from objects (Python/Ruby/Java/C#)?

I'd like to create a simple table programatically. I have some objects and I'd like to map some properties to headers and the collection to rows.

Why Markdown? Because I'd like to edit that document manually later. Right now, the whole process looks like this:

  • reporting engine is in C#
  • there are objects from which DOCX are generated (there is intermediate XML or something like that)
  • almost always I have to do minor fixes and I have to open that docx documents in MS Word
  • it's troublesome to ask the developer team to fix every single bug, because they simply have no time to do it instantly and I have to wait for next release.

I've figured out that if I would get Markdown document, I could edit it easily, insert some variables and use pandoc to replace those variables with given data. But to get Markdown I have to know how the devs could generate tables in Markdown.

Indochina answered 15/11, 2012 at 8:56 Comment(4)
You don't mention Haskell. If you can write a simple script in Haskell, you can use the pandoc API (in particular Text.Pandoc.Builder) to create tables quite easily, and write them to any format supported by pandoc, including markdown.Hades
i didn't mention that, because I don't know haskell. yet. Hmm right now I was looking for ready solution. <headers, rows> => markdown table. If there is no such solution, I'll try to script something like that, but how the hell can I integrate haskell in .NET code? Need to do some research...Indochina
Unfortunately Markdown doesn't (yet?) support tables, but some of the extensions do. In particular, Doxygen has a nice table extension for Markdown.Edette
The R pander package is what you're looking for.Gao
E
13

I needed to do just about the same thing to generate Doxygen Markdown tables, so I thought I'd share. I've run the example code successfully in both Python 2.7 and 3.3, although I can't claim I've tested it rigorously.

# Generates tables for Doxygen flavored Markdown.  See the Doxygen
# documentation for details:
#   http://www.doxygen.nl/manual/markdown.html#md_tables

# Translation dictionaries for table alignment
left_rule = {'<': ':', '^': ':', '>': '-'}
right_rule = {'<': '-', '^': ':', '>': ':'}

def evalute_field(record, field_spec):
    """
    Evalute a field of a record using the type of the field_spec as a guide.
    """
    if type(field_spec) is int:
        return str(record[field_spec])
    elif type(field_spec) is str:
        return str(getattr(record, field_spec))
    else:
        return str(field_spec(record))

def table(file, records, fields, headings, alignment = None):
    """
    Generate a Doxygen-flavor Markdown table from records.

    file -- Any object with a 'write' method that takes a single string
        parameter.
    records -- Iterable.  Rows will be generated from this.
    fields -- List of fields for each row.  Each entry may be an integer,
        string or a function.  If the entry is an integer, it is assumed to be
        an index of each record.  If the entry is a string, it is assumed to be
        a field of each record.  If the entry is a function, it is called with
        the record and its return value is taken as the value of the field.
    headings -- List of column headings.
    alignment - List of pairs alignment characters.  The first of the pair
        specifies the alignment of the header, (Doxygen won't respect this, but
        it might look good, the second specifies the alignment of the cells in
        the column.

        Possible alignment characters are:
            '<' = Left align (default for cells)
            '>' = Right align
            '^' = Center (default for column headings)
    """

    num_columns = len(fields)
    assert len(headings) == num_columns

    # Compute the table cell data
    columns = [[] for i in range(num_columns)]
    for record in records:
        for i, field in enumerate(fields):
            columns[i].append(evalute_field(record, field))

    # Fill out any missing alignment characters.
    extended_align = alignment if alignment != None else []
    if len(extended_align) > num_columns:
        extended_align = extended_align[0:num_columns]
    elif len(extended_align) < num_columns:
        extended_align += [('^', '<')
                           for i in range[num_columns-len(extended_align)]]

    heading_align, cell_align = [x for x in zip(*extended_align)]

    field_widths = [len(max(column, key=len)) if len(column) > 0 else 0
                    for column in columns]
    heading_widths = [max(len(head), 2) for head in headings]
    column_widths = [max(x) for x in zip(field_widths, heading_widths)]

    _ = ' | '.join(['{:' + a + str(w) + '}'
                    for a, w in zip(heading_align, column_widths)])
    heading_template = '| ' + _ + ' |'
    _ = ' | '.join(['{:' + a + str(w) + '}'
                    for a, w in zip(cell_align, column_widths)])
    row_template = '| ' + _ + ' |'

    _ = ' | '.join([left_rule[a] + '-'*(w-2) + right_rule[a]
                    for a, w in zip(cell_align, column_widths)])
    ruling = '| ' + _ + ' |'

    file.write(heading_template.format(*headings).rstrip() + '\n')
    file.write(ruling.rstrip() + '\n')
    for row in zip(*columns):
        file.write(row_template.format(*row).rstrip() + '\n')

Here's a simple test case:

import sys

sys.stdout.write('State Capitals (source: Wikipedia)\n\n')

headings = ['State', 'Abrev.', 'Capital', 'Capital since', 'Population',
            'Largest Population?']

data = [('Alabama', 'AL', '1819', 'Montgomery', '1846', 155.4, False,
         205764),
        ('Alaska', 'AK', '1959', 'Juneau', '1906', 2716.7, False, 31275),
        ('Arizona', 'AZ', '1912', 'Phoenix', '1889',474.9, True, 1445632),
        ('Arkansas', 'AR', '1836', 'Little Rock', '1821', 116.2, True,
         193524)]

fields = [0, 1, 3, 4, 7, lambda rec: 'Yes' if rec[6] else 'No']

align = [('^', '<'), ('^', '^'), ('^', '<'), ('^', '^'), ('^', '>'),
         ('^','^')]

table(sys.stdout, data, fields, headings, align)

Gives this output:

State Capitals (source: Wikipedia)

|  State   | Abrev. |   Capital   | Capital since | Population | Largest Population? |
| :------- | :----: | :---------- | :-----------: | ---------: | :-----------------: |
| Alabama  |   AL   | Montgomery  |     1846      |     205764 |         No          |
| Alaska   |   AK   | Juneau      |     1906      |      31275 |         No          |
| Arizona  |   AZ   | Phoenix     |     1889      |    1445632 |         Yes         |
| Arkansas |   AR   | Little Rock |     1821      |     193524 |         Yes         |

Doxygen renders this as:

Sample

Edette answered 16/3, 2013 at 5:29 Comment(3)
Your table() function uses str() without giving the encoding. When used with Unicode input data, it will raise UnicodeEncodeErrorsCassandry
@AlastairMcCormack, it's been awhile, but it must have been Python 2.7 code.Edette
It's more of a note for other people :) I recently answered someone's question who'd referenced this answer but was using Unicode strings :)Cassandry
K
1

I had a requirement to produce Markdown programmatically for a recent project so I built a library and posted it on GitHub. Hopefully, you'll find it useful.

The project is called MarkdownLog and it is a lightweight (i.e. minimal dependencies), portable .NET library (PCL) that can produce Markdown from .NET data structures, such as collections and dictionaries. I use it for logging internal program data structures for diagnostic purposes but it should meet your needs also.

Here's how a Markdown table is built from a collection:

var data = new[]
{
    new{Year = 1991, Album = "Out of Time", Songs=11, Rating = "* * * *"},
    new{Year = 1992, Album = "Automatic for the People", Songs=12, Rating = "* * * * *"},
    new{Year = 1994, Album = "Monster", Songs=12, Rating = "* * *"}
};

Console.Write(data.ToMarkdownTable());

// Produces:
//
//     Year | Album                    | Songs | Rating   
//     ----:| ------------------------ | -----:| --------- 
//     1991 | Out of Time              |    11 | * * * *  
//     1992 | Automatic for the People |    12 | * * * * *
//     1994 | Monster                  |    12 | * * *    

Note that when this output is parsed with a GitHub-flavoured Markdown parser it will produce a HTML table.

By default, columns are aligned based on their datatype (numbers are right-aligned, strings are left aligned) and the header names are produced from the object's property names. If this isn't what you want, there's a number of overrides that give you more control over the output.

There's built in support for all the standard Markdown elements and also GFM tables. I've also added some extra element types (Bar Chart, iOS UITableView) that I needed, which are implemented as code blocks so they still adhere to the Markdown standard.

I've only recently uploaded the code to GitHub so the documentation is basic for now. Having said this, there are loads of unit tests in the project, which should demonstrate how it works.

I appreciate it's been a while since this question was asked but I hope this project will be useful to somebody.

Kovrov answered 8/7, 2014 at 17:33 Comment(1)
I forgot to say, I'd love to hear any feedback or suggestionsKovrov
W
0

Using Apache Freemarker to render a HTML by Java code, then convert it to Markdown. Here is away to transfer HTML table to markdown.

Flexmark-java to convert HTML table to Markdown

Whereabouts answered 26/7, 2021 at 11:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.