Javascript 给定一个数据矩阵,计算html行跨度和列跨度

Javascript 给定一个数据矩阵,计算html行跨度和列跨度,javascript,Javascript,我有一个稀疏矩阵,如下所示,由数据单元(1..9)和空单元(=零)组成: 我想将其显示为html表,但不应该有空单元格-它们应该被相邻数据单元格的行“覆盖”-和colspan: <table border=1 cellpadding=10> <tr> <td rowspan=2>1</td> <td colspan=2>2</td> <td>3</td

我有一个稀疏矩阵,如下所示,由数据单元(1..9)和空单元(=零)组成:

我想将其显示为html表,但不应该有空单元格-它们应该被相邻数据单元格的行“覆盖”-和colspan:

<table border=1 cellpadding=10>
    <tr>
        <td rowspan=2>1</td>
        <td colspan=2>2</td>
        <td>3</td>
    </tr>
    <tr>
        <td colspan=3>4</td>
    </tr>
    <tr>
        <td>5</td>
        <td>6</td>
        <td>7</td>
        <td>8</td>
    </tr>
</table>

1.
2.
3.
4.
5.
6.
7.
8.

(这是一种可能的实现,我们也可以在第二行使用
colspan=4
,而不使用rowspan)

生成实际的html不是问题,但我在编写算法来计算数据单元的行和列跨度时遇到了困难

编辑:仍在为此寻找答案。仅使用colspan并将每个数据单元与其左/右上的空单元连接起来似乎很简单。然而,我希望单元格尽可能呈正方形,因此答案也应该包括行跨度逻辑。谢谢


EDIT2:到目前为止的所有答案总结如下:

根据您的示例,colspan是一个加上非空单元格右侧的空单元格数,而rowspan类似地是一个加上非空单元格下方的空单元格数


更有趣的是,一个非空单元格的右边和下面都有空单元格。您希望如何处理这种情况?

在Java脚本中,您必须执行以下操作

var str="<table border=1 cellpadding=10>"
var rows=data.lenght();
if(rows>0)
{
    for(int r=0;r<rows;r++)
    {
        var cols=data[r].lenght();
        if(cols>0)
        {
            str+="<tr>";
            for(int c=0;c<cols;c++)
            {
                var colstospan=getnext0(r,c,0); //This function will give you columns to be span... defination given below
                str+="<td colspan="+colstospan+">"+data[r][c]+"</td>";
            }
            str+="</tr>";
        }
    }
}
str+="</table>";


str will give you table you require...
我希望这对你有用。。。。 如果
lenght()函数中出现语法错误,请忽略…

注意
当我写这个答案时,我还没有仔细阅读您的评论,您在评论中表达了对sqare表示法的偏好(即:colspan=2 rowspan=2表示3)。一旦我找到时间,我会增加一个更灵活的答案

一个简单的循环方法可能就足以解决这个问题:

var cells = [
    [ 1, 2, 0, 3 ],
    [ 0, 4, 0, 0 ],
    [ 5, 6, 7, 8 ]
],i, j, c, r,tmpString, tbl = [];
for (i=0;i<cells.length;++i)
{
    tbl[i] = [];
    for (j=0;j<cells[i].length;++j)
    {
        if (cells[i][j] !== 0)
        {
            if (j === 0)
            {//This only works for first indexes (if not, cells[0][3] + cells[1][3] yields rowspan, too)
                r = 1;
                while(i+r < cells.length && cells[i+r][j] === 0)
                    ++r;//while next row == 0, add rowspan count
            }
            c = 1;
            while(c+j < cells[i].length && cells[i][j+c] === 0)
                ++c;
            tmpString = '<td';
            if (j === 0 && r > 1)
                tmpString += ' rowspan="' + r + '"';
            if (c > 1)
                tmpString += ' colspan="' + c + '"';
            tmpString += '>'+cells[i][j]+'</td>';
            tbl[i].push(tmpString);
        }
    }
    tbl[i] = tbl[i].join('');
}
console.log('<table border=1 cellpadding=10><tr>' + tbl.join('</tr><tr>') + '</tr></table>');
无论如何,添加一些缩进后的输出如下:

<table border=1 cellpadding=10>
    <tr>
        <td rowspan="2">1</td>
        <td colspan="2">2</td>
        <td>3</td>
    </tr>
    <tr>
        <td colspan="3">4</td>
    </tr>
    <tr>
        <td>5</td>
        <td>6</td>
        <td>7</td>
        <td>8</td>
    </tr>
</table>

1.
2.
3.
4.
5.
6.
7.
8.

我想说,这与你追求的东西非常接近,你想要它尽可能的方形吗?我想说的是,你要优先考虑最大的领域。 现在是算法。首先,让我们把矩阵放大一点:

这就是我想到的:

它使用以下简单步骤:

  • 如果要在矩阵中找到矩形,它将计算矩形必须具有的所有可能的宽度和高度
  • 它首先根据这些值的平方与否对其进行排序,对平方进行优先级排序,然后根据面积进行排序:从大面积到小面积
  • 然后,它尝试适应每个矩形,从大到小
  • 如果矩形中有1个数字,且至少有1个零,则矩形“适合”
  • 如果合适的话,他会在那个里留下一个标记,所以这个矩形后面的所有矩形都不能使用那个些单元格
  • 当没有零时结束
  • 完成了

    var Omatrix=[//原始矩阵
    [ 1, 2, 0, 3, 4, 0, 2 ],
    [ 0, 4, 0, 0, 7, 0, 4 ],
    [ 5, 6, 7, 8, 0, 1, 0 ],
    [ 1, 0, 0, 0, 4, 0, 0 ],
    [ 0, 4, 0, 0, 7, 2, 4 ],
    [ 5, 0, 0, 0, 0, 3, 0 ],
    [ 0, 0, 0, 0, 4, 9, 3 ],
    ];
    var矩阵=[]
    对于(变量i=0;ivar cells = [
        [ 1, 2, 0, 3 ],
        [ 0, 4, 0, 0 ],
        [ 5, 6, 7, 8 ]
    ],i, j, c, r,tmpString, tbl = [];
    for (i=0;i<cells.length;++i)
    {
        tbl[i] = [];
        for (j=0;j<cells[i].length;++j)
        {
            if (cells[i][j] !== 0)
            {
                if (j === 0)
                {//This only works for first indexes (if not, cells[0][3] + cells[1][3] yields rowspan, too)
                    r = 1;
                    while(i+r < cells.length && cells[i+r][j] === 0)
                        ++r;//while next row == 0, add rowspan count
                }
                c = 1;
                while(c+j < cells[i].length && cells[i][j+c] === 0)
                    ++c;
                tmpString = '<td';
                if (j === 0 && r > 1)
                    tmpString += ' rowspan="' + r + '"';
                if (c > 1)
                    tmpString += ' colspan="' + c + '"';
                tmpString += '>'+cells[i][j]+'</td>';
                tbl[i].push(tmpString);
            }
        }
        tbl[i] = tbl[i].join('');
    }
    console.log('<table border=1 cellpadding=10><tr>' + tbl.join('</tr><tr>') + '</tr></table>');
    
    tmp[i].push('<td' + (j === 0 && r > 1 ? ' rowspan="' + r + '"' : '')
        + (c > 1 ? ' colspan="' + c +'"' : '') + '>' + cells[i][j] + '</td>'
    );
    
    <table border=1 cellpadding=10>
        <tr>
            <td rowspan="2">1</td>
            <td colspan="2">2</td>
            <td>3</td>
        </tr>
        <tr>
            <td colspan="3">4</td>
        </tr>
        <tr>
            <td>5</td>
            <td>6</td>
            <td>7</td>
            <td>8</td>
        </tr>
    </table>
    
    var Omatrix = [   //Original matrix
        [ 1, 2, 0, 3, 4, 0, 2 ],
        [ 0, 4, 0, 0, 7, 0, 4 ],
        [ 5, 6, 7, 8, 0, 1, 0 ],
        [ 1, 0, 0, 0, 4, 0, 0 ],
        [ 0, 4, 0, 0, 7, 2, 4 ],
        [ 5, 0, 0, 0, 0, 3, 0 ],
        [ 0, 0, 0, 0, 4, 9, 3 ],
    ];
    
    var matrix = []
    for (var i = 0; i < Omatrix.length; i++){
        matrix[i] = Omatrix[i].slice(0);
    }
    
    //calculating all possible lengths
    var a = matrix[0].length;
    //maximum rectangle width
    var b = matrix.length;
    //maximum rectangle height
    
    //calculate the area of each rectangle and save it using an array
    var array = [];
    for (var i = 1; i <= a; i++) {
        for (var j = 1; j <= b; j++) {
            array.push({"width":i, "height":j, "area":i*j, "square":i===j});
        }
    }
    
    //sort first on square, then on area: biggest first
    array.sort(function(a, b){
        if(a.square === b.square){
            x = b.area;
            y = a.area;
            return ((x < y) ? -1 : ((x > y) ? 1 : 0));
        }
        else{
            return a.square? -1:1;
        }
    });    
    
    for(var i = 0; i < array.length; i++){
        //working from biggest area to smallest
        for(var j = 0; j < matrix.length-array[i].width+1; j++){
            //working from left to right
            notfound:
            for(var k = 0; k < matrix[0].length-array[i].height+1; k++){
                //working from top to bottom
                var nonzero = false;
                var zero = false
                //checking if there is exactly one number and atleast one zero
                for(var l = 0; l < array[i].width; l++){
                    //working from left to right
                    for(var m = 0; m < array[i].height; m++){
                        //working from top to bottom
                        if(matrix[j+l][k+m] > 0){
                            if(!nonzero){
                                //we have found the first number 
                                //and saved that number for later use
                                nonzero = matrix[j+l][k+m];
                            }
                            else{
                                //we have found a second number:
                                //break and go to the next square
                                continue notfound;
                            }
                        }
                        else if(matrix[j+l][k+m] === 0){
                            //this is a zero
                            zero = true;
                        }
                        else{
                            //there is already a square build from this block
                            continue notfound;
                        }
                    }
                }
                if(!nonzero || !zero){
                    //there isn't a number here
                    //or there isn't a zero here
                    continue notfound;
                }
    
                //mark the spots with '-'
                for(var l = 0; l < array[i].width; l++){
                    for(var m = 0; m < array[i].height; m++){
                        matrix[j+l][k+m] = "-";
                    }
                }
                //pack the head of the block with data
                matrix[j][k] = array[i].width+"x"+array[i].height+"x"+nonzero;
            }
        }
    }
    
    var tablestring = "";
    for(var i = 0; i < Omatrix.length; i++){
        tablestring += "<tr>";
        for(var j = 0; j < Omatrix[i].length; j++){
            tablestring += "<td>"+Omatrix[i][j]+"</td>"; 
        }
        tablestring += "</tr>";
    }
    
    document.getElementById("table1").innerHTML += tablestring;
    
    var tablestring = "";
    for(var i = 0; i < Omatrix.length; i++){
        tablestring += "<tr>";
        for(var j = 0; j < Omatrix[i].length; j++){
            //going trough all the cells
            if(matrix[i][j] === "-"){
                //a cell with a "-" will not be displayed
                continue;
            }
            else if(typeof(matrix[i][j]) === "string"){
                //a cell with a string is the head of a big block of cells.
                var add = "";
                var data = matrix[i][j].split("x");
                if(data[0] !== "1"){
                    add += " rowspan="+data[0];
                }
                if(data[1] !== "1"){
                    add += " colspan="+data[1];
                }
                tablestring += "<td"+add+">"+data[2]+"</td>"; 
            }
            else{
                //a normal cell
                tablestring += "<td>"+Omatrix[i][j]+"</td>"; 
            }
        }
        tablestring += "</tr>";
    }
    
    document.getElementById("table2").innerHTML += tablestring;
    
    [1, 2, 3, 4, 5],
    [7, 0, 0, 0, 0],
    [0, 0, 0, 0, 0],
    [1, 2, 0, 0, 5]
    
    [1] [2] [3] [4] [5] instead of [1] [2] [3] [4] [5]
    [  7  ] [ ] [ ] [ ]            [        7        ]
    [     ] [ ] [ ] [ ]            [                 ]
    
    [1, 1, 2, 0],   gives   [1] [1] [  2  ]
    [3, 0, 0, 0],           [         ] [?] <---
    [0, 0, 0, 5],           [    3    ] [5]
    [0, 0, 0, 1]            [         ] [1]
    
                                                 V
    [1, 0, 0, 2],  will leave you with  [  1  ] [?] [2]
    [0, 0, 3, 4],                       [     ] [3] [4]
    [0, 5, 6, 7]                    --->[?] [5] [6] [7]
    
    var maxv = arr.length;
    var maxh = arr[0].length;
    var rsv = [];
    var rsh = [];
    pop_arr();
    var shape;
    try_sq();
    try_rect();
    try_loose();
    
    
    //TRY SQUARES FIRST
    function try_sq() {
        var sv, sh;
        shape = [];
        for (sv = 0; sv < maxv - 1; sv++) {
            for (sh = 0; sh < maxh - 1; sh++) {
                if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) {
                    continue;
                }
                check_sq(sv, sh);
            }
        }
        if (shape.length > 0) {
            occu();
        }
    }
    
    
    //TRY RECTANGLES
    function try_rect() {
        var sv, sh;
        var rect = false;
        do {
            shape = [];
            for (sv = 0; sv < maxv; sv++) {
                for (sh = 0; sh < maxh; sh++) {
                    if (arr[sv][sh] === 0 || rsv[sv][sh] > -1 || rsh[sv][sh] > -1) continue;
                    check_rec(sv, sh);
                }
            }
            if (shape.length > 0) {
                occu();
            } else {
                rect = false;
            }
        } while (rect === true);
    }
    
    //TRY LOOSE
    function try_loose() {
        var sv, sh;
        //SET THE 1x1 with value
        for (sv = 0; sv < maxv; sv++) {
            for (sh = 0; sh < maxh; sh++) {
                if (arr[sv][sh] !== 0 && (rsv[sv][sh] == -1 || rsh[sv][sh] == -1)) {
                    rsv[sv][sh] = 1;
                    rsh[sv][sh] = 1;
                }
            }
        }
        //SEARCH FOR rectangles wit no value
        var rect = true;
        do {
            shape = [];
            for (sv = 0; sv < maxv; sv++) {
                for (sh = 0; sh < maxh; sh++) {
                    if (arr[sv][sh] !== 0 || (rsv[sv][sh] > -1 || rsh[sv][sh] > -1)) {
                        continue;
                    }
                    rect = check_loose(sv, sh);
                }
            }
            if (shape.length > 0) occu();
            else {
                rect = false;
            }
        } while (rect === true);
    }
    
    
    //check SINGLES 
    function check_loose(start_v, start_h) {
        var vd, hd, iv, ih, rect;
        var hor = ver = 1;
        var horb = 0;
        var mxv = maxv - 1;
        var mxh = maxh - 1;
        rect = true;
        vd = start_v + ver;
        hd = start_h + hor;
    
        //check horizontal
        for (sh = start_h + 1; sh <= mxh; sh++) {
            if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) {
                break;
            }
            hor++;
        }
        //check vertical
        for (iv = start_v + 1; iv <= mxv; iv++) {
            if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) {
                break;
            }
            ver++;
        }
        if (hor > ver || hor == ver) {
            shape.unshift({
                0: (hor),
                1: [start_v, start_h, 1, hor]
            });
            return true;
        } else if (ver > hor) {
            shape.push({
                0: (ver),
                1: [start_v, start_h, ver, 1]
            });
            return true;
        }
        return false;
    }
    
    
    
    
    //check SQUARE        
    function check_sq(start_v, start_h) {
        if (arr[start_v + 1][start_h] !== 0) {
            return false;
        }
        if (arr[start_v][start_h + 1] !== 0) {
            return false;
        }
        var vd, hd, sv, sh, square;
        var hor = ver = 1;
        var mxv = maxv - 1;
        var mxh = maxh - 1;
        //CHECK DIAGONAL
        do {
            square = true;
            vd = start_v + ver;
            hd = start_h + hor;
            //diagonal OK
            if (arr[vd][hd] !== 0) {
                if (hor == 1) {
                    if (ver == 1) {
                        return false;
                    }
                    square = false;
                    break;
                }
            }
            //check horizontal
            for (sh = start_h; sh <= hd; sh++) {
                if (arr[vd][sh] !== 0) {
                    square = false;
                    break;
                }
            }
            if (square === false) break;
            //check vertical
            for (sv = start_v; sv <= vd; sv++) {
                if (arr[sv][hd] !== 0) {
                    square = false;
                    break;
                }
            }
            if (square === false) break;
            hor++;
            ver++;
        } while (square === true && vd < mxv && hd < mxh);
        //SQUARE OK
        if (hor > 1 && ver > 1 && hor == ver) {
            shape.push({
                0: (hor * ver),
                1: [start_v, start_h, ver, hor]
            });
        }
    }
    
    
    //check RECTANGLE
    function check_rec(start_v, start_h) {
        var vd, hd, iv, ih, rect;
        var hor = ver = 1;
        var horb = 0;
        var mxv = maxv - 1;
        var mxh = maxh - 1;
        rect = true;
        vd = start_v + ver;
        hd = start_h + hor;
    
        //check horizontal
        if (start_h < maxh) {
            for (sh = start_h + 1; sh <= mxh; sh++) {
                if (arr[start_v][sh] !== 0 || rsh[start_v][sh] > -1) break;
                hor++;
            }
        }
        //check vertical
        if (start_v < maxv) {
            for (iv = start_v + 1; iv <= mxv; iv++) {
                if (arr[iv][start_h] !== 0 || rsh[iv][start_h] > -1) break;
                ver++;
            }
        }
        if (hor == 1 && ver == 1) return false;
        if (hor > ver || hor == ver) {
            shape.unshift({
                0: (hor),
                1: [start_v, start_h, 1, hor]
            });
            return true;
        } else {
            shape.push({
                0: (ver),
                1: [start_v, start_h, ver, 1]
            });
            return true;
        }
        return false;
    }
    
    
    //FIND LARGEST SHAPE
    function occu() {
        var le = shape.length;
        for (var i = 0; i < le; i++) {
            var b = Math.max.apply(Math, shape.map(function (v) {
                return v[0];
            }));
            for (var j = 0; j < shape.length; j++) {
                if (shape[j][0] == b) break;
            }
            var c = shape.splice(j, 1);
            claim(c[0][1]);
        }
    }
    
    
    //CLAIM SHAPE
    function claim(sh) {
        var iv, ih;
        for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) {
            for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) {
                if (rsv[iv][ih] > -1 || rsh[iv][ih] > -1) return false;
            }
        }
        for (iv = sh[0]; iv < sh[0] + sh[2]; iv++) {
            for (ih = sh[1]; ih < sh[1] + sh[3]; ih++) {
                rsv[iv][ih] = 0;
                rsh[iv][ih] = 0;
            }
        }
        rsv[sh[0]][sh[1]] = sh[2];
        rsh[sh[0]][sh[1]] = sh[3];
    }
    
    function pop_arr() {
        var em = [];
        em[0] = arr[0].concat();
        for (var i = 0; i < maxh; i++) {
            em[0][i] = -1;
        }
        for (i = 0; i < maxv; i++) {
            rsv[i] = em[0].concat();
            rsh[i] = em[0].concat();
        }
    }
    
    var matrix = [
      [ 1, 2, 0, 3 ],
      [ 0, 4, 0, 0 ],
      [ 5, 6, 7, 8 ],
    ];
    
    var table = matrix.reduce(function(rows, row){
      rows.push(row.reduce(function(newRow, col){
        var lastCol = newRow[newRow.length - 1];
        if (!col && lastCol) {
          lastCol.colspan = (lastCol.colspan || 1) + 1;
        } else if (!col && !lastCol) {
          var lastRowCol = rows[rows.length-1][newRow.length];
          if (lastRowCol) lastRowCol.rowspan = (lastRowCol.rowspan || 1) + 1;
        } else {
          newRow.push({value:col});
        }
        return newRow;
      }, []));
      return rows;
    }, []);
    
    
    var expected =  [
      [{value : 1, rowspan: 2}, {value:2, colspan: 2}, {value:3}],
      [{value : 4, colspan: 3}],
      [{value : 5}, {value:6}, {value:7}, {value:8}]
    ];
    
    table.should.eql(expected); // Passed OK