1

I need to generate html table from 2d array containing only data, colspan and rowspan. I have no need for margins, heights and widths. Only td and tr. I know table cell width and height, result is always rectangular. I need to do it in java but any hint is welcome.

Here is an example cell class:

public class Cell {

    String label;
    int colSpan, rowSpan;

    public Cell(String label, int colSpan, int rowSpan) {
        this.label = label;
        this.colSpan = colSpan;
        this.rowSpan = rowSpan;
    }

}

And here is an example of 2d array containing data and rowspan/colspan information;

public class Test {

    public static void main(String[] args) {
        List<List<Cell>> rows = new ArrayList<>();

        List<Cell> row1 = new ArrayList<>();
        Cell cell11 = new Cell("total", 1, 9);
        Cell cell12 = new Cell("mid_total", 1, 3);
        Cell cell13 = new Cell("detail", 1, 1);
        row1.add(cell11);
        row1.add(cell12);
        row1.add(cell13);
        rows.add(row1);

        List<Cell> row2 = new ArrayList<>();
        Cell cell21 = new Cell("mid_total", 1, 3);
        Cell cell22 = new Cell("detail", 1, 1);
        row2.add(cell21);
        row2.add(cell22);
        rows.add(row2);

        List<Cell> row3 = new ArrayList<>();
        Cell cell31 = new Cell("mid_total", 1, 3);
        Cell cell32 = new Cell("detail", 1, 1);
        row3.add(cell31);
        row3.add(cell32);
        rows.add(row3);

        List<Cell> row4 = new ArrayList<>();
        Cell cell41 = new Cell("detail", 1, 1);
        row4.add(cell41);
        rows.add(row4);

        List<Cell> row5 = new ArrayList<>();
        Cell cell51 = new Cell("detail", 1, 1);
        row5.add(cell51);
        rows.add(row5);

        List<Cell> row6 = new ArrayList<>();
        Cell cell61 = new Cell("detail", 1, 1);
        row6.add(cell61);
        rows.add(row6);

        List<Cell> row7 = new ArrayList<>();
        Cell cell71 = new Cell("detail", 1, 1);
        row7.add(cell71);
        rows.add(row7);

        List<Cell> row8 = new ArrayList<>();
        Cell cell81 = new Cell("detail", 1, 1);
        row8.add(cell81);
        rows.add(row8);

        List<Cell> row9 = new ArrayList<>();
        Cell cell91 = new Cell("detail", 1, 1);
        row9.add(cell91);
        rows.add(row9);
    }
}

And here's how it should look like:

+-------+-----------+--------+
| total | mid_total | detail |
+       +           +--------+
|       |           | detail |
+       +           +--------+
|       |           | detail |
+       +-----------+--------+
|       | mid_total | detail |
+       +           +--------+
|       |           | detail |
+       +           +--------+
|       |           | detail |
+       +-----------+--------+
|       | mid_total | detail |
+       +           +--------+
|       |           | detail |
+       +           +--------+
|       |           | detail |
+-------+-----------+--------+
2
  • Are you looking for a general way to determine rowspans and colspans or for the more special case outlined in the example? I ask, because your table doesn't use variable colspans and looks more like a tree with its root to te left, which may suggest a data structure other than a grid. Commented Jan 12, 2016 at 12:09
  • More like a general way. There are far more complex examples than do not look like tree structure at all. Commented Jan 12, 2016 at 12:20

3 Answers 3

1

iterate your rows and columns in your 2d array and create the HTML tags using a StringBuilder.

public String toHTML(String[][] data){
    StringBuilder sb = new StringBuilder();
    sb.append("<table>\n");
    for(int row = 0; row < data.length; row++){
        sb.append("\t<tr>\n");
        for(int col = 0; col < data[0].length; col++){
            sb.append("\t\t<td>" + data[row][col] + "</td>\n");
        }
        sb.append("\t</tr>\n");
    }
    sb.append("</table>");
    return sb.toString();
}

If you use it like this:

String[][] data = {{"r0c1", "r0c2", "r0c3"},
                   {"r1c1", "r1c2", "r1c3"}};   
System.out.println(toHTML(data));

It will produce this:

<table>
    <tr>
        <td>r0c1</td>
        <td>r0c2</td>
        <td>r0c3</td>
    </tr>
    <tr>
        <td>r1c1</td>
        <td>r1c2</td>
        <td>r1c3</td>
    </tr>
</table>
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks for a quick answer, but my 2d array contains rowspan and colspan information also.
it would be easier to help if you post an example of your 2d array that you want to convert.
0

You could create a container class for the cells, which manages the correct positioning of multi-column and multi-row cells.

You have a grid of slots and when you place a cell that span several columns or rows, you invalidate the slots that this big cell occupies, keeping a reference to the real cell only in the top left slot. When you print the markup, don't print invalidated cells.

You seem to create your tables top down, so a natural approach would be to keep track of the current row and columns, so that inserting a cell can find the next free one.

If you don't want to dimension the table beforehand or jiggle an ever-resizing grid of cells, you can store the cells in a hash map with their position in the grid as key, so that you can easily append cells without worrying to overflow the table.

An implementation with your example table is below. I'm not good with Java – I lack both the skills and the software – so I've writen the code in D. The principal idea should be clear, I think. D has associative arrays built in and V[K] corresponds to Java's java.util.HashMap<K, V>.

import std.stdio;
import std.array;
import std.string;

class Cell
{
public:
    this(string _label, int _colSpan, int _rowSpan)
    {
        label = _label;
        colSpan = _colSpan;
        rowSpan = _rowSpan;
    }

    string toHtml() {
        auto app = appender!string();

        app.put("        <td");
        if (colSpan > 1) app.put(format(" colspan='%d'", colSpan));
        if (rowSpan > 1) app.put(format(" rowspan='%d'", rowSpan));
        app.put(">");
        app.put(label);
        app.put("</td>\n");

        return app.data;
    } 

private:
    string label;
    int colSpan;
    int rowSpan;
}

class Table
{
public:
    this() {
    }

    void newRow() {
        row++;
        col = 0;
    }

    void add(string label, int colSpan = 1, int rowSpan = 1)
    {
        assert(row >= 0);
        assert(colSpan > 0);
        assert(rowSpan > 0);

        auto c = new Cell(label, colSpan, rowSpan);

        while (key(col, row) in cell) col++;

        for (int j = 0; j < rowSpan; j++) {
            for (int i = 0; i < colSpan; i++) {
                addCell(col + i, row + j,
                    (i == 0 && j == 0) ? c : null);
            }
        }

        col += colSpan;
    }

    string toHtml() {
        auto app = appender!string();

        app.put("<table>\n");

        for (int y = 0; y < nrow; y++) {
            app.put("    <tr>\n");

            for (int x = 0; x < ncol; x++) {

                if (key(x, y) !in cell) {
                    app.put("        <td></td>\n");
                } else {
                    Cell c = cell[key(x, y)];

                    if (c) app.put(c.toHtml());
                }
            }
            app.put("    </tr>\n");
        }
        app.put("</table>");

        return app.data;
    }

private:
    void addCell(int x, int y, Cell c) {
        Cell *p = key(x, y) in cell;

        if (x + 1 > ncol) ncol = x + 1;
        if (y + 1 > nrow) nrow = y + 1;

        assert(p is null);
        cell[key(x, y)] = c;
    }

    static uint key(uint x, uint y)
    {
        const offset = 1u << 16;
        return y * offset + x;
    }


    Cell[uint] cell;        // map of positions to cells
    int row = -1;           // current row
    int col;                // current column
    int nrow;               // number of rows
    int ncol;               // number of columns
};

void main()
{
    auto tbl = new Table();

    tbl.newRow();
    tbl.add("Total", 1, 9);
    tbl.add("Subtotal A-C", 1, 3);
    tbl.add("detail A");

    tbl.newRow();
    tbl.add("detail B");

    tbl.newRow();
    tbl.add("detail C");

    tbl.newRow();
    tbl.add("Subtotal D-F", 1, 3);
    tbl.add("detail D");

    tbl.newRow();
    tbl.add("detail E");

    tbl.newRow();
    tbl.add("detail F");

    tbl.newRow();
    tbl.add("Subtotal G-I", 1, 3);
    tbl.add("detail G");

    tbl.newRow();
    tbl.add("detail H");

    tbl.newRow();
    tbl.add("detail I");

    writeln(tbl.toHtml());   
}

2 Comments

Thanks for a reply. I have rewriten your implementation in java and it works for your source rows. Please try it with my source rows in first post above and you will see that it fails to generate table as it should.
How are my source rows different from yours? The method of table generation is different, but the data is pretty much the same. I have tested my method with some other layouts and it worked for me. Anyway, I'm not going to spoonfeed you a solution tailored to your requirements, which are not very clear from the question. It seems that you didn't know how to approach this problem and I've given you an idea, which you can hopefully build on.
0

This is how my source rows look like, try it and see it yourself.

auto tbl = new Table();

tbl.newRow();
tbl.add("Total", 1, 9);
tbl.add("Subtotal A-C", 1, 3);
tbl.add("detail A");

tbl.newRow();
tbl.add("Subtotal D-F", 1, 3);
tbl.add("detail B");

tbl.newRow();
tbl.add("Subtotal G-I", 1, 3);
tbl.add("detail C");

tbl.newRow();
tbl.add("detail D");

tbl.newRow();
tbl.add("detail E");

tbl.newRow();
tbl.add("detail F");

tbl.newRow();
tbl.add("detail G");

tbl.newRow();
tbl.add("detail H");

tbl.newRow();
tbl.add("detail I");

writeln(tbl.toHtml());

4 Comments

This is not an answer to your question. If you want to clarify the requirements, please edit your question.
You have posted this a s a case where my proposed answer doesn't work. If you expect the same output as with your original layout, I have to concede that it doesn't work, but that's because the Table class isn't used as it should. If you want to start a cell in the fourth row, you can't add it in the second row. I thought that your problem was to account for the multi-column spans. My answer does that, but it doesn't create a layout for you; you have to think about the layout yourself.
Maybe I didn't clarify question in the first place. I can't change the order source rows are coming. So I need an algorithm that will generate proper layout.
In your example, it looks as if you knew in which column the new cells belong: "total" goes into the first columns, "subtotal *" into the next and "detail *" into the last. You can easily modify the algorithm to suit your needs by removing the newRow method and instead telling the add mathod which column the cell should go to. Look for the first available slot (for which cell[key(col, row)] doesn't exist) and call addCell(...). Actually the public add is just a frontend to the private core method addCell.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.