自定义合并table行/列

本文介绍了一种处理动态生成表格中单元格合并的方法,通过数据预处理将原始数据转换为目标结构,以便直接绘制出合并后的表格。核心内容包括数据格式化、行列合并逻辑和创建目标表格的详细步骤。

前言

我们有如下的一组原始数据:

 const data = [
            ['阶段', '年份', '投入费用', '投入费用', '合计'],
            ['构建期', 2012, 1212, 1300, 29],
            ['构建期', 2013, 154, 154, 29],
            ['运营期', 2014, 154, 154, 484]
        ];

用原生table将其绘制出来:

 function createTable() {
            const table = document.createElement('table');
            const thead = document.createElement('thead');
            const tbody = document.createElement('tbody');
            for (let i = 0; i < data.length; i++) {
                const tr = document.createElement('tr');
                i === 0 ? thead.appendChild(tr) : tbody.appendChild(tr);
                for (let j = 0; j < data[0].length; j++) {
                    if (i === 0) {
                        const th = document.createElement('th');
                        th.innerText = data[i][j];
                        tr.appendChild(th);
                    } else {
                        const td = document.createElement('td');
                        td.innerText = data[i][j];
                        tr.appendChild(td);
                    }
                }
            }
            table.appendChild(thead);
            table.appendChild(tbody);
            document.body.appendChild(table);
        }

结果如图:
在这里插入图片描述
观察table不难发现有一些连续的单元格是相同的数据,现在我们需要按照指定的行/列将其进行单元格合并,如下图的合并结果:
在这里插入图片描述
那么我们此时应该如何处理呢?

处理思路

由于是通过后台数据动态生成表格,所以创建表格的方式是通过js代码动态创建。
按照一种容易想到的思路,我们可以先按照数据创建出表格,然后在已经创建的表格的基础上进行单元格合并,这个思路很清晰,但是仔细想想,第二步的合并单元格的道路似乎很棘手甚至于无从下手,并且还需要对表格的结构进行重构。既然如此,那为何我们不直接先处理好原始数据,然后一次性将目标表格绘制出来呢。

数据预处理

我们发现我们无法通过原始数据直接绘制出目标表格,因为原始数据不能给我们提供每个单元格有关是否需要进行合并的信息,所以我们需要进行加工,比如得到如下的目标数据:

 const formatTable = [
            [
                {
                    "value": "阶段",
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": "年份",
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": "投入费用",
                    "rowCount": 2,
                    "colCount": 1
                },
                {
                    "value": "投入费用",
                    "rowCount": 0,
                    "colCount": 1
                },
                {
                    "value": "合计",
                    "rowCount": 1,
                    "colCount": 1
                }
            ],
            [
                {
                    "value": "构建期",
                    "rowCount": 1,
                    "colCount": 2
                },
                {
                    "value": 2012,
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 1212,
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 1300,
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 29,
                    "rowCount": 1,
                    "colCount": 2
                }
            ],
            [
                {
                    "value": "构建期",
                    "rowCount": 1,
                    "colCount": 0
                },
                {
                    "value": 2013,
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 154,
                    "rowCount": 2,
                    "colCount": 1
                },
                {
                    "value": 154,
                    "rowCount": 0,
                    "colCount": 1
                },
                {
                    "value": 29,
                    "rowCount": 1,
                    "colCount": 0
                }
            ],
            [
                {
                    "value": "运营期",
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 2014,
                    "rowCount": 1,
                    "colCount": 1
                },
                {
                    "value": 154,
                    "rowCount": 2,
                    "colCount": 1
                },
                {
                    "value": 154,
                    "rowCount": 0,
                    "colCount": 1
                },
                {
                    "value": 484,
                    "rowCount": 1,
                    "colCount": 1
                }
            ]
        ];

目标数据中包含每个单元格的行合并信息以及列合并信息,如此一来我们便能通过处理后的数据绘制出目标表格结构。
下面我们来看看数据预处理的思路:一种比较直观的思路是,分别得到行合并的处理数据以及列合并的处理数据,最后再将两组数据合并为一组数据,也就是目标数据。至于如何进行具体的处理,这部分逻辑不难,大家直接看代码即可。

 function formatDataCol(targetCols) {
            const formatTable = new Array(data.length).fill(0).map(() => new Array(data[0].length).fill(0));
            for (let j = 0; j < data[0].length; j++) {
                if (targetCols.indexOf(j) === -1) {
                    for (let i = 0; i < data.length; i++) {
                        formatTable[i][j] = {
                            value: data[i][j],
                            colCount: 1
                        };
                    }
                } else {
                    let index = 0;
                    let tmp = data[index][j];
                    let colCount = 1;
                    for (let i = 1; i < data.length; i++) {
                        if (data[i][j] === tmp) {
                            colCount++;
                            formatTable[i][j] = {
                                value: data[i][j],
                                colCount: 0
                            };
                        } else {
                            formatTable[index][j] = {
                                value: data[index][j],
                                colCount: colCount,
                            };
                            index = i;
                            tmp = data[index][j];
                            colCount = 1;
                        }
                    }
                    if (index < data.length) {
                        formatTable[index][j] = {
                            value: data[index][j],
                            colCount: colCount,
                        };
                    }
                }
            }
            return formatTable;
        }

        function formatDataRow(targetRows) {
            const formatTable = new Array(data.length).fill(0).map(() => new Array(data[0].length).fill(0));
            for (let i = 0; i < data.length; i++) {
                if (targetRows.indexOf(i) === -1) {
                    for (let j = 0; j < data[0].length; j++) {
                        formatTable[i][j] = {
                            value: data[i][j],
                            rowCount: 1
                        };
                    }
                } else {
                    let index = 0;
                    let tmp = data[i][index];
                    let rowCount = 1;
                    for (let j = 1; j < data[0].length; j++) {
                        if (data[i][j] === tmp) {
                            rowCount++;
                            formatTable[i][j] = {
                                value: data[i][j],
                                rowCount: 0
                            };
                        } else {
                            formatTable[i][index] = {
                                value: data[i][index],
                                rowCount: rowCount,
                            };
                            index = j;
                            tmp = data[i][index];
                            rowCount = 1;
                        }
                    }
                    if (index < data[0].length) {
                        formatTable[i][index] = {
                            value: data[i][index],
                            rowCount: rowCount,
                        };
                    }
                }
            }
            return formatTable;
        }

        function mergeFormat(formatCols, formatRows) {
            const formatTable = new Array(data.length).fill(0).map(() => new Array(data[0].length).fill(0));
            for (let i = 0; i < data.length; i++) {
                for (let j = 0; j < data[0].length; j++) {
                    formatTable[i][j] = {
                        value: data[i][j],
                        rowCount: formatRows[i][j].rowCount,
                        colCount: formatCols[i][j].colCount
                    };
                }
            }
            return formatTable;
        }

创建目标表格

到此,我们已经得到了目标数据,只剩下最后一步创建目标表格了。
这一步逻辑也比较直接,稍微复杂一点的就是需要根据colCount以及rowCount的状态分类进行逻辑处理。
show you the last code:

function createTableByData(targetCols, targetRows) {
            const formatCols = formatDataCol(targetCols);
            const formatRows = formatDataRow(targetRows);
            const data = mergeFormat(formatCols, formatRows);
            const table = document.createElement('table');
            const thead = document.createElement('thead');
            const tbody = document.createElement('tbody');
            for (let i = 0; i < data.length; i++) {
                const tr = document.createElement('tr');
                i === 0 ? thead.appendChild(tr) : tbody.appendChild(tr);
                for (let j = 0; j < data[0].length; j++) {
                    if (i === 0) {
                        const th = document.createElement('th');
                        if (data[i][j].rowCount === 0) {

                        } else if (data[i][j].rowCount === 1) {
                            th.innerText = data[i][j].value;
                            tr.appendChild(th);
                        } else {
                            th.innerText = data[i][j].value;
                            th.colSpan = data[i][j].rowCount;
                            tr.appendChild(th);
                        }
                    } else {
                        const td = document.createElement('td');
                        if (data[i][j].colCount === 0 || data[i][j].rowCount === 0) {

                        } else if (data[i][j].colCount === 1 && data[i][j].rowCount === 1) {
                            td.innerText = data[i][j].value;
                            tr.appendChild(td);
                        } else if (data[i][j].colCount !== 1 && data[i][j].rowCount !== 1) {
                            td.innerText = data[i][j].value;
                            td.rowSpan = data[i][j].colCount;
                            td.colSpan = data[i][j].rowCount;
                            tr.appendChild(td);
                        } else if (data[i][j].colCount !== 1 && data[i][j].rowCount === 1) {
                            td.innerText = data[i][j].value;
                            td.rowSpan = data[i][j].colCount;
                            tr.appendChild(td);
                        } else if (data[i][j].colCount === 1 && data[i][j].rowCount !== 1) {
                            td.innerText = data[i][j].value;
                            td.colSpan = data[i][j].rowCount;
                            tr.appendChild(td);
                        }
                    }
                }
            }
            table.appendChild(thead);
            table.appendChild(tbody);
            document.body.appendChild(table);
        }
        createTableByData([0, 4], [0, 1, 2, 3]);

在这里我指定的需要合并的行是第一行和第五行,需要合并的列有第一列、第二列、第三列以及第四列。所以最终会呈现出开头目标的表格效果。

结语

到此,自定义的合并表格行/列的算法已经完成了。这个算法的代码实现难度不大,但是其处理思路还是有点意思的。如果有大佬还有更好的算法思路,欢迎评论区留言。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值