Converting DataTable to HTML--Research on the Method of Realizing Automatic Merging of Cells

In a relational database, data is often stored in the form of a list, but in fact there is a logical relationship between the parallel data in the physical structure, such as the tree structure of the parent-child relationship. Similarly, when the data stored in the database is displayed on the interface, similar relationships need to be displayed, such as the commonly used cell merging in tables.


1. Under what conditions can cells be merged

The merging of cells in Excel is controlled by the user, and there are no preconditions, but if you want to realize the merging of cells through code, you must have a series of rules that can be read by the computer. A common situation where cells need to be merged is: multiple adjacent cells have the same value, and the adjacent area should be a rectangle, such as width and height 1 2, 2 1, 4*2 , etc.
insert image description here

2. Convert DataTable to HTML and automatically merge cells

The back-end code traverses the data in the DataTable, stitches them into HTML, merges cells during processing, and outputs to the front-end.

/// <summary>
 /// 数据表转换为HTML并自动合并单元格
 /// </summary>
 /// <param name="dt">数据表</param>
 /// <param name="tableId">标识</param>
 /// <param name="isAddSort">是否添加序号</param>
 /// <returns></returns>
 public string ConvertTableToHtmlWithMergeCells(DataTable dt,string tableId,bool isAddSort)
 {
    
    
     //先行后列,建立单元格值字典
     Dictionary<string, List<int[]>> dicInit = new Dictionary<string, List<int[]>>();
     for (int i = 0; i < dt.Rows.Count; i++)
     {
    
    
         for (int j = 0; j < dt.Columns.Count; j++)
         {
    
    
             if (!string.IsNullOrWhiteSpace(dt.Rows[i][j].ToString()))
             {
    
    
                 var k = dt.Rows[i][j].ToString();
                 if (!dicInit.ContainsKey(k))
                 {
    
    
                     dicInit.Add(k, new List<int[]> {
    
     new int[2] {
    
     i, j } });
                 }
                 else
                 {
    
    
                     var v = dicInit[k];
                     dicInit.Remove(k);
                     v.Add(new int[2] {
    
     i, j });
                     dicInit.Add(k, v);
                 }
             }
         }
     }
     //过滤出具有相同值且需要合并的单元格
     Dictionary<string, List<int[]>> dic = new Dictionary<string, List<int[]>>();
     foreach (var k in dicInit)
     {
    
    
         if (IsNeedMerge(k.Value))
         {
    
    
             dic.Add(k.Key, k.Value);
         }
     }

     //构造数据表
     StringBuilder htmlBuilder = new StringBuilder();
     htmlBuilder.Append("<table cellspacing=\"0\" cellpadding=\"0\" width=\"100% \" id=\"" + tableId+"\">");
     htmlBuilder.Append("<tbody>");
     
     //首行
     htmlBuilder.Append("<tr class=\"firstRow\">");
     if (isAddSort)//是否增加序号列
     {
    
    
         htmlBuilder.Append("<td>序号</td>");
     }
     for (int i = 0; i < dt.Columns.Count; i++)//列
     {
    
    
         htmlBuilder.Append("<td>");
         htmlBuilder.Append(dt.Columns[i].ColumnName);
         htmlBuilder.Append("</td>");
     }
     htmlBuilder.Append("</tr>");

     //遍历数据表并拼接内容
     for (int i=0;i<dt.Rows.Count;i++)//行
     {
    
    
         htmlBuilder.Append("<tr>");
         int rowNum = i + 1;//行序号
         if (isAddSort)//是否增加行序号
         {
    
    
             htmlBuilder.Append("<td>"+rowNum+"</td>");
         }
         for (int j=0;j<dt.Columns.Count;j++)//列
         {
    
    
             string cellValue = dt.Rows[i][j].ToString();
             if (!string.IsNullOrEmpty(cellValue)&&dic.ContainsKey(cellValue))
             {
    
    
                 var arrys = dic[cellValue];
                 int[] firstCell = arrys[0];
                 int[] lastCell = arrys[arrys.Count - 1];
                 int row = lastCell[0] - firstCell[0] + 1;//行数
                 int col = lastCell[1] - firstCell[1] + 1;//列数
                 if (i == firstCell[0] && j == firstCell[1])//需要合并的单元格中,只保留首行首列的一个
                 {
    
    
                     htmlBuilder.Append("<td rowspan=\""+row+"\" colspan=\""+col+"\">");
                     htmlBuilder.Append(cellValue);
                     htmlBuilder.Append("</td>");
                 }
             }
             else
             {
    
    
                 htmlBuilder.Append("<td>");
                 htmlBuilder.Append(cellValue);
                 htmlBuilder.Append("</td>");
             }
         }
         htmlBuilder.Append("</tr>");
     }

     htmlBuilder.Append("</tbody>");
     htmlBuilder.Append("</table>");
     string strHtml = htmlBuilder.ToString().Replace("\"", "");
     return strHtml;
 }

 /// <summary>
 /// 判断是否需要合并单元格
 /// </summary>
 /// <param name="arrys"></param>
 /// <returns></returns>
 private bool IsNeedMerge(List<int[]> arrys)
 {
    
    
     bool result = false;
     int num = arrys.Count;//具有相同值的单元格总数
     if (num>1)//移除只出现值只出现过一次的单元格
     {
    
    
         int[] firstCell = arrys[0];
         int[] lastCell = arrys[arrys.Count - 1];
         int row = lastCell[0] - firstCell[0] + 1;//行数
         int col = lastCell[1] - firstCell[1] + 1;//列数
         if (row * col == num)//若行数与列数之积等于单元格总数,则说明具有相同值的单元格相邻分布,可以合并
         {
    
    
             result = true;
         }
     }

     return result;
 }

The idea here is somewhat similar to the inverted index, which creates an index table for all non-null values ​​in the data table, and records the position where each value appears in the data table. First, perform a filter to remove the value that appears only once in the index table. If it only appears once, it is definitely not necessary to merge cells, and it can also reduce the query process. Then loop through the data table and start splicing HTML strings. If the value of the cell is in the index table, it means that the cell needs to be spliced. At the same time, the position of the value in the index table is recorded in the order of rows and columns, so it can be ensured that the first position in the position list is the first cell that needs to be merged, as long as the cross-row and cross-column attributes of this cell are set. Can.


It should be noted that this method also has the following problems:

  • It is only applicable to a single segment of continuous cells with the same value, and multiple consecutive cells with intervals cannot be merged separately. The idea of ​​further optimization is that the index table records the interval step between two cells with the same value on the basis of the recording position.
  • Applies only to cell values ​​of type string. The idea of ​​further optimization is that for the cell values ​​of the numeric type, either do not merge, or in addition to merging the cells, the sum of the numbers is also calculated.

Guess you like

Origin blog.csdn.net/lordwish/article/details/124939068