【问题标题】:How to calculate row- and colspans dynamically?如何动态计算行和列跨度?
【发布时间】:2022-01-04 17:50:49
【问题描述】:

我正在使用来自 UI 框架的表格组件,它为我生成以下标题

基于此配置

(最终结果)

[
  [
    {
      "title": "Section 1",
      "rowspan": "4",
      "colspan": "1"
    },
    {
      "title": "Section 2",
      "rowspan": "1",
      "colspan": "2"
    },
    {
      "title": "Section 3",
      "rowspan": "1",
      "colspan": "3"
    },
    {
      "title": "Section 4",
      "rowspan": "1",
      "colspan": "1"
    }
  ],
  [
    {
      "title": "Section 2.1",
      "rowspan": "3",
      "colspan": "1"
    },
    {
      "title": "Section 2.2",
      "rowspan": "3",
      "colspan": "1"
    },
    {
      "title": "Section 3.1",
      "rowspan": "3",
      "colspan": "1"
    },
    {
      "title": "Section 3.2",
      "rowspan": "1",
      "colspan": "2"
    },
    {
      "title": "Section 4.1",
      "rowspan": "1",
      "colspan": "1"
    }
  ],
  [
    {
      "title": "Section 3.2.1",
      "rowspan": "2",
      "colspan": "1"
    },
    {
      "title": "Section 3.2.2",
      "rowspan": "2",
      "colspan": "1"
    },
    {
      "title": "Section 4.1.1",
      "rowspan": "1",
      "colspan": "1"
    }
  ],
  [
    {
      "title": "Section 4.1.1.1",
      "rowspan": "1",
      "colspan": "1"
    }
  ]
]

我想简化配置。应该只有一个树状结构,我想动态计算行和列跨度

(起点)

[
  {
    "title": "Section 1"
  },
  {
    "title": "Section 2",
    "children": [
      {
        "title": "Section 2.1"
      },
      {
        "title": "Section 2.2"
      }
    ]
  },
  {
    "title": "Section 3",
    "children": [
      {
        "title": "Section 3.1"
      },
      {
        "title": "Section 3.2",
        "children": [
          {
            "title": "Section 3.2.1"
          },
          {
            "title": "Section 3.2.2"
          }
        ]
      }
    ]
  },
  {
    "title": "Section 4",
    "children": [
      {
        "title": "Section 4.1",
        "children": [
          {
            "title": "Section 4.1.1",
            "children": [
              {
                "title": "Section 4.1.1.1"
              }
            ]
          }
        ]
      }
    ]
  }
]

我想过如何将起点数组映射到最终结果数组。这些是我的假设:

  • 递归遍历数组
  • 像这样计算 colspan(宽度)
    • 如果没有孩子 => 1
    • 如果有任何孩子 => 下一个深度的所有孩子的 colspan 的总和
  • 计算行跨度(高度)
    • 如果没有孩子 => 整个树的最大深度 - 当前深度
    • 如果有孩子 => 1

我目前的做法

const configuredHeaders = [{
    title: "Section 1"
},
{
    title: "Section 2",
    children: [{
            title: "Section 2.1"
        },
        {
            title: "Section 2.2"
        }
    ]
},
{
    title: "Section 3",
    children: [{
            title: "Section 3.1"
        },
        {
            title: "Section 3.2",
            children: [{
                    title: "Section 3.2.1"
                },
                {
                    title: "Section 3.2.2"
                }
            ]
        }
    ]
},
{
    title: "Section 4",
    children: [{
        title: "Section 4.1",
        children: [{
            title: "Section 4.1.1",
            children: [{
                title: "Section 4.1.1.1"
            }]
        }]
    }]
}];

const finalHeaders = [];

traverseColumnHeaders(configuredHeaders, -1); // first depth should start with 0

function traverseColumnHeaders(headers, parentDepth) {
    headers.forEach(header => {
        header.currentDepth = parentDepth + 1;
        
        if (header.children) {
            traverseColumnHeaders(header.children, header.currentDepth);
            
            header.colspan = header.children.reduce((currentSum, child) => currentSum + child.colspan, 0); // sum of child colspans
            header.rowspan = 1;
        } else {
            header.colspan = 1;

            // header.rowspan =     ... maximum depth - header.currentDepth ...
        }

        addHeaderToFinalHeaders(header);
    });
}

function addHeaderToFinalHeaders({ currentDepth, title, rowspan, colspan }) {
    const depthExistsInFinalHeaders = currentDepth in finalHeaders;
    
    if (!depthExistsInFinalHeaders) {
        finalHeaders[currentDepth] = [];
    }

    finalHeaders[currentDepth].push({ title, rowspan, colspan });
}

console.log(finalHeaders);

这个对我来说看起来不错,因为我只需要遍历集合一次。但正如你所见,一些rowspan 字段尚未计算,因为我不知道此时的最大深度。

为了解决这个问题,我可以初始化一个全局变量,像这样搜索最大深度

(我暂时从这里 Getting the depth of a tree data structure in a simpler way 获取解决方案)

function getMaximumDepth(headers) {
    return getDepth(headers) -1;
};

function getDepth(headers) {
    return 1 + Math.max(0, ...headers.map(({ children = [] }) => getDepth(children)));
}

const maximumDepth = getMaximumDepth(configuredHeaders);

计算

header.rowspan = maximumDepth - header.currentDepth

但现在我必须遍历结构两次。有没有办法优化我的解决方案?

【问题讨论】:

    标签: javascript


    【解决方案1】:

    我相信没有合理的方法可以仅通过一次迭代来完成您想做的事情。您要完成的前提之一是单元格必须跨越行到树的深度。因此,这是在遍历数组以将其转换为 finalHeaders 形式时必须作为先决条件存在的知识。

    随着年龄的增长,我发现我宁愿优化代码的清晰度,而不是一些执行问题。显然我并没有忽略效率问题,但即使是手机在循环小数据集时也非常快。但我必须忍受和维护代码。如果我写一些“高效”的迟钝代码,我会在 6 个月后诅咒自己,而且我必须花 45 分钟试图记住它是如何工作的。

    此外,您只遍历结构一次的目标已经是不可能的。要确定colspan,您正在使用reduce,它本身正在迭代所有的孩子。此外,任何显示代码都将遍历数据。所以已经有很多迭代正在进行。我反对以代码清晰为代价在一次迭代中想出一些聪明的方法来完成此任务。

    以下是一个工作示例。这基本上就是您在问题中概述的内容,我可能会进行一些小调整,但对逻辑没有实质性改变:

    const configuredHeaders = [{
        title: "Section 1"
      },
      {
        title: "Section 2",
        children: [{
            title: "Section 2.1"
          },
          {
            title: "Section 2.2"
          }
        ]
      },
      {
        title: "Section 3",
        children: [{
            title: "Section 3.1"
          },
          {
            title: "Section 3.2",
            children: [{
                title: "Section 3.2.1"
              },
              {
                title: "Section 3.2.2"
              }
            ]
          }
        ]
      },
      {
        title: "Section 4",
        children: [{
          title: "Section 4.1",
          children: [{
            title: "Section 4.1.1",
            children: [{
              title: "Section 4.1.1.1"
            }]
          }]
        }]
      }
    ];
    
    const maximumDepth = getMaximumDepth(configuredHeaders);
    const finalHeaders = [];
    
    traverseColumnHeaders(configuredHeaders);
    
    function traverseColumnHeaders(headers, depth = 0) {
      headers.forEach(header => {
        header.depth = depth;
    
        if (header.children) {
          traverseColumnHeaders(header.children, depth + 1);
          header.colspan = header.children.reduce((currentSum, child) => currentSum + child.colspan, 0); // sum of child colspans
          header.rowspan = 1;
        } else {
          header.colspan = 1;
          header.rowspan = maximumDepth - depth;
        }
    
        addHeaderToFinalHeaders(header);
      });
    }
    
    function addHeaderToFinalHeaders({depth, title, rowspan, colspan}) {
      if (!finalHeaders[depth]) finalHeaders[depth] = [];
      finalHeaders[depth].push({title, rowspan, colspan});
    }
    
    function getMaximumDepth(headers) {
      return getDepth(headers) - 1;
    
      function getDepth(headers) {
        return 1 + Math.max(0, ...headers.map(({children = []}) => getDepth(children)));
      }
    }
    
    //console.log(finalHeaders);
    document.body.insertAdjacentHTML('beforeend', table(finalHeaders));
    
    function table(headers) {
      return `
    <table>
        <thead>
            ${headers.map(header => `
            <tr>${header.map(cell => `<th colspan="${cell.colspan}" rowspan="${cell.rowspan}">${cell.title}</th>`).join('')}</tr>
            `).join('')}
        </thead>
    </table>`;
    }
    table, th {
      border: 1px solid black;
    }

    【讨论】:

      猜你喜欢
      • 2011-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多