简体   繁体   English

给定一个数据矩阵,计算html rowspan和colspan

[英]Given a data matrix, calculate html rowspan and colspan

I have a sparse matrix like below consisting of data cells (1..9) and empty cells (=zero): 我有一个如下所示的稀疏矩阵,包括数据单元格(1..9)和空单元格(= 0):

[
    [ 1, 2, 0, 3 ],
    [ 0, 4, 0, 0 ],
    [ 5, 6, 7, 8 ],
]

I'd like to display this as an html table, but there should be no empty cells - they should be "covered" by their neighbouring data cells' row- and colspans: 我想将它显示为一个html表,但是应该没有空单元格 - 它们应该被它们的相邻数据单元的行和colspans“覆盖”:

<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>

在此输入图像描述

(this is one possible implementation, we could also use colspan=4 on the second row and no rowspan). (这是一种可能的实现,我们也可以在第二行使用colspan=4而没有rowspan)。

Generating actual html is not a problem, but I have trouble writing an algorithm to calculate row and column spans for data cells. 生成实际的html不是问题,但是我在编写算法来计算数据单元的行和列跨度时遇到了麻烦。

EDIT : still looking for an answer for this. 编辑 :仍然在寻找答案。 It seems to be trivial to work with colspans only and concatenate each data cell with empty cells on its left/right. 仅使用colspans并在每个数据单元的左/右连接空单元格似乎是微不足道的。 However, I'd like cells to be as square shaped as possible, so the answer should include rowspan logic as well. 但是,我希望单元格尽可能为方形,所以答案应该包括rowspan逻辑。 Thanks! 谢谢!

EDIT2 : all answers so far summarized here: http://jsfiddle.net/ThQt4/ EDIT2 :迄今为止总结的所有答案: http//jsfiddle.net/ThQt4/

You want it as square-shaped as possible? 你想要它尽可能的方形? I'd say, you want to prioritize the biggest area. 我想说,你想优先考虑最大的区域。 Now to the algoritm. 现在到了算法。 First, let's make the matrix a little bit bigger: 首先,让矩阵变大一点:

jsFiddle 的jsfiddle

And this is where I came up with: 这就是我想出的地方:

jsFiddle 的jsfiddle

It's using these simple steps: 它使用这些简单的步骤:

  1. It calculates all possible widths and heights a rectangle has to have if it wants to find in the matrix. 它计算矩形必须具有的所有可能的宽度和高度,如果它想要在矩阵中找到。
  2. It sorts these values first on if their square or not, prioritizing squares, then on area: from a big area to a small one. 它首先对这些值进行排序,如果它们的正方形与否,优先正方形,然后是区域:从大区域到小区域。
  3. Then it tries to fit in each rectangle, from big to small. 然后它尝试适合每个矩形,从大到小。
  4. A rectangle "fits" if there is 1 number in the rectangle, and at least 1 zero. 如果矩形中有1个数字,则矩形“适合”,并且至少为1。
  5. If it fits, he leaves a mark there, so all rectangle after this one can't use those cells. 如果它适合,他会在那里留下一个标记,所以在这之后的所有矩形都不能使用那些单元格。
  6. It ends when there are no zero's left. 当没有零时,它结束。

Done. 完成。

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;

There are a few things to consider in what you want. 你想要的东西要考虑几件事。

If you always prefer squares over the biggest shape, you could end up with a lot of single rows an colums, because a rectangle always starts with a square. 如果你总是喜欢在最大的形状上使用正方形,那么最终可能会有很多单行的柱子,因为矩形总是以正方形开头。 And a square or rectangle can only start if the top-left part has a value. 正方形或矩形只能在左上角部分有值时才能开始。

Consider this array: 考虑这个数组:

[1, 2, 3, 4, 5],
[7, 0, 0, 0, 0],
[0, 0, 0, 0, 0],
[1, 2, 0, 0, 5]

If you choose squares over the biggest shape you'll end up with a square and three columns 如果你在最大的形状上选择正方形,你最终会得到一个正方形和三列

[1] [2] [3] [4] [5] instead of [1] [2] [3] [4] [5]
[  7  ] [ ] [ ] [ ]            [        7        ]
[     ] [ ] [ ] [ ]            [                 ]

Furthermore, you could end up with empty single cells: 此外,您可能最终得到空单个单元格:

[1, 1, 2, 0],   gives   [1] [1] [  2  ]
[3, 0, 0, 0],           [         ] [?] <---
[0, 0, 0, 5],           [    3    ] [5]
[0, 0, 0, 1]            [         ] [1]

The largest square here is size 3x3 starting with 3. If you first claim the 3x3 and then the row starting with 2, you'll end up with an empty cell [?] above 5. 这里最大的正方形是从3开始的3x3大小。如果你首先声明3x3然后是从2开始的行,你将得到一个高于5的空单元格[?]

One last thing, if you have zeros in the top row or left column, you could also end up in trouble: 最后一件事,如果你在顶行或左列中有零,你也可能会遇到麻烦:

                                             V
[1, 0, 0, 2],  will leave you with  [  1  ] [?] [2]
[0, 0, 3, 4],                       [     ] [3] [4]
[0, 5, 6, 7]                    --->[?] [5] [6] [7]

That said, maybe your array isn't that complicated and maybe you don't mind the empty cells. 也就是说,也许你的数组并不复杂,也许你不介意空单元格。 In either way, it was fun solving this puzzle with your specs. 无论哪种方式,使用您的规格解决这个难题很有趣。 My solution obeys the following rules: 我的解决方案遵循以下规则:

1) squares go first. 1)方块先行。

2) Size matters. 2)尺寸问题。 The bigger, the better 越大越好

3) Rows and colums: Size matters. 3)行和列:大小很重要。 The biggest go first. 最大的先行。

4) Colspan goes over rowspan 4)Colspan越过了rowpan

Fiddle 小提琴

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();
    }
}

Interesting question. 有趣的问题。 This works in most browsers. 这适用于大多数浏览器。 It uses the array method reduce which is perfect for these type of problems. 它使用数组方法reduce,这对于这些类型的问题是完美的。

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

Note 注意
When I wrote this answer, I hadn't yet read through your comments, where you expressed a preference for sqare representations (ie: colspan=2 rowspan=2 for 3). 当我写这个答案时,我还没有读过你的评论,你表达了对sqare表示的偏好(即:colspan = 2 rowspan = 2 for 3)。 I'll add a more flexible answer once I find the time. 一旦找到时间,我会添加一个更灵活的答案。

A simple loop approach might just be enough to tackle this: 一个简单的循环方法可能足以解决这个问题:

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>');

The varnames are clumsy (at best), but the names are i and j are just simple index/loop counter variables. varnames是笨拙的(充其量),但名称是ij只是简单的索引/循环计数器变量。 c counts the colspans that'll be required at any given time, and r does the same for the rowspans. c计算在任何给定时间都需要的colspans, r对rowpans执行相同的操作。 tbl is an is an array of arrays that'll be used to build the table string. tbl是一个数组数组,用于构建表字符串。
tmpString is just a variable that is used to concat each cell together. tmpString只是一个用于将每个单元连接在一起的变量。 Its main reason of existence is to avoid horrid statements like: 其存在的主要原因是避免可怕的陈述,如:

tmp[i].push('<td' + (j === 0 && r > 1 ? ' rowspan="' + r + '"' : '')
    + (c > 1 ? ' colspan="' + c +'"' : '') + '>' + cells[i][j] + '</td>'
);

Anyway, the output, after some indentation has been added is this: 无论如何,添加了一些缩进后的输出是这样的:

<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>

Which is, I'd say, pretty close to what you were after 我会说,这与你追求的非常接近

From your example, the colspan is the one plus the number of empty cells to the right of a none empty cell and the rowspan is similarly one plus the number of empty cells below a none empty cell. 从您的示例中,colspan是一个加上无空单元格右侧的空单元格数,并且rowspan类似于一个加上无空单元格下方的空单元格数。

Where things get more interesting is when a none empty cell has empty cells both to the right and below. 事情变得更有趣的是当没有空单元格在右侧和下方都有空单元格时。 How do you want to handle that situation? 你想怎么处理这种情况?

Here In your Java script you have to do following 在您的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...

Here is the Defination of getnext0 这是getnext0的定义

function getnext0(rows,cols,count)
{
    if(data[rows][cols+1]==0)
    {
        return getnext0(rows,cols+1,count+1);
    }
    return count;
}

I hope this will work for you.... please ignore if syntax error in lenght() function... 我希望这对你有用....请忽略lenght()函数中的语法错误...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM