如何将JSON矩阵转换为携带有表头属性的单个数据单元格记录?
问题
给定一个由拉丁语动词变位表生成的JSON矩阵,其中:
- JSON对象为数据单元格
- 字符串单元格为表头单元格
null单元格可忽略
表头具有以下特性:
- 数据单元格上方存在多级表头
- 数据单元格左侧存在多级表头
- 当某行全为字符串(无数据单元格)时,上方表头会重置
需求:将对应表头应用到每个数据单元格,输出一个数组,每个数据单元格对象添加所属表头的布尔属性(属于该表头则设为true)。
示例:
- 左上角首个数据单元格处理后:
{ "la": "accēdō", "active": true, "present": true, "indicative": true, "singular": true, "first": true }
- 底部中间的某个数据单元格处理后:
{ "gerund": true, "dative": true, "la": "accēdendō", "verbal nouns": true }
如何遍历该矩阵,正确将左侧和上方的表头应用到数据单元格?
算法实现思路与代码
核心逻辑
- 维护表头状态:分别记录当前有效的左侧多级表头和上方多级表头,遇到全字符串行时重置上方表头。
- 逐行遍历矩阵:
- 先判断当前行是否为全字符串行:若是,更新上方表头并跳过数据处理;若不是,提取当前行的左侧表头,合并到左侧表头状态中。
- 遍历当前行的每个单元格:如果是数据对象(非字符串、非null),则将当前所有左侧和上方表头对应的布尔属性添加到该对象中,收集到结果数组。
代码实现
function processConjugationMatrix(matrix) { const result = []; // 存储当前有效的上方多级表头:每一列对应一个表头数组 let columnHeaders = Array.from({ length: matrix[0].length }, () => []); // 存储当前有效的左侧多级表头:当前行的左侧表头集合 let currentRowLeftHeaders = []; for (const row of matrix) { // 判断当前行是否为全字符串行(无数据对象和null) const isFullHeaderRow = row.every(cell => typeof cell === 'string'); if (isFullHeaderRow) { // 重置上方表头:每一列的表头数组替换为当前行对应列的字符串 row.forEach((cell, colIndex) => { columnHeaders[colIndex] = [cell]; }); // 全表头行无左侧数据表头,清空当前状态 currentRowLeftHeaders = []; continue; } // 提取当前行的左侧表头:前几个连续的字符串单元格 const leftHeaders = []; for (const cell of row) { if (typeof cell === 'string') { leftHeaders.push(cell); } else { break; } } // 更新当前左侧表头状态 if (leftHeaders.length > 0) { currentRowLeftHeaders = leftHeaders; } // 遍历当前行单元格,处理数据对象 row.forEach((cell, colIndex) => { if (cell && typeof cell === 'object') { // 复制原数据对象,避免修改原矩阵 const processedCell = { ...cell }; // 添加左侧表头的布尔属性 currentRowLeftHeaders.forEach(header => { processedCell[header] = true; }); // 添加上方表头的布尔属性 columnHeaders[colIndex].forEach(header => { processedCell[header] = true; }); result.push(processedCell); } }); } return result; } // 测试用矩阵(题目给定的变位表矩阵) const conjugationMatrix = [ [ "indicative" , "indicative" , "singular" , "singular" , "singular" , "plural" , "plural" , "plural" ], [ "indicative" , "indicative" , "first" , "second" , "third" , "first" , "second" , "third" ], [ "active" , "present" , {"la":"accēdō"} , {"la":"accēdis"} , {"la":"accēdit"} , {"la":"accēdimus"} , {"la":"accēditis"} , {"la":"accēdunt"} ], [ "active" , "imperfect" , {"la":"accēdēbam"} , {"la":"accēdēbās"} , {"la":"accēdēbat"} , {"la":"accēdēbāmus"} , {"la":"accēdēbātis"} , {"la":"accēdēbant"} ], [ "active" , "future" , {"la":"accēdam"} , {"la":"accēdēs"} , {"la":"accēdet"} , {"la":"accēdēmus"} , {"la":"accēdētis"} , {"la":"accēdent"} ], [ "active" , "perfect" , {"la":"accessī"} , {"la":"accessistī"} , {"la":"accessit"} , {"la":"accessimus"} , {"la":"accessistis"} , {"la":"accessēre"} ], [ "active" , "pluperfect" , {"la":"accesseram"} , {"la":"accesserās"} , {"la":"accesserat"} , {"la":"accesserāmus"} , {"la":"accesserātis"} , {"la":"accesserant"} ], [ "active" , "future perfect" , {"la":"accesserō"} , {"la":"accesseris"} , {"la":"accesserit"} , {"la":"accesserimus"} , {"la":"accesseritis"} , {"la":"accesserint"} ], [ "subjunctive" , "subjunctive" , "singular" , "singular" , "singular" , "plural" , "plural" , "plural" ], [ "subjunctive" , "subjunctive" , "first" , "second" , "third" , "first" , "second" , "third" ], [ "active" , "present" , {"la":"accēdam"} , {"la":"accēdās"} , {"la":"accēdat"} , {"la":"accēdāmus"} , {"la":"accēdātis"} , {"la":"accēdant"} ], [ "active" , "imperfect" , {"la":"accēderem"} , {"la":"accēderēs"} , {"la":"accēderet"} , {"la":"accēderēmus"} , {"la":"accēderētis"} , {"la":"accēderent"} ], [ "active" , "perfect" , {"la":"accesserim"} , {"la":"accesserīs"} , {"la":"accesserit"} , {"la":"accesserīmus"} , {"la":"accesserītis"} , {"la":"accesserint"} ], [ "active" , "pluperfect" , {"la":"accessissem"} , {"la":"accessissēs"} , {"la":"accessisset"} , {"la":"accessissēmus"} , {"la":"accessissētis"} , {"la":"accessissent"} ], [ "imperative" , "imperative" , "singular" , "singular" , "singular" , "plural" , "plural" , "plural" ], [ "imperative" , "imperative" , "first" , "second" , "third" , "first" , "second" , "third" ], [ "active" , "present" , null , {"la":"accēde"} , null , null , {"la":"accēdite"} , null ], [ "active" , "future" , null , {"la":"accēditō"} , {"la":"accēditō"} , null , {"la":"accēditōte"} , {"la":"accēduntō"} ], [ "non-finite forms" , "non-finite forms" , "active" , "active" , "active" , "passive" , "passive" , "passive" ], [ "non-finite forms" , "non-finite forms" , "present" , "perfect" , "future" , "present" , "perfect" , "future" ], [ "infinitives" , "infinitives" , {"la":"accēdere"} , {"la":"accessisse"} , {"la":"accessūrum esse"} , null , null , null ], [ "participles" , "participles" , {"la":"accēdēns"} , null , {"la":"accessūrus"} , null , null , null ], [ "verbal nouns" , "verbal nouns" , "gerund" , "gerund" , "gerund" , "gerund" , "supine" , "supine" ], [ "verbal nouns" , "verbal nouns" , "genitive" , "dative" , "accusative" , "ablative" , "accusative" , "ablative" ], [ "verbal nouns" , "verbal nouns" , {"la":"accēdendī"} , {"la":"accēdendō"} , {"la":"accēdendum"} , {"la":"accēdendō"} , {"la":"accessum"} , {"la":"accessū"} ] ]; // 执行处理并输出结果 const processedResult = processConjugationMatrix(conjugationMatrix); console.log(JSON.stringify(processedResult, null, 2));
代码说明
- 表头状态维护:
columnHeaders:数组结构,每个元素对应一列的多级表头集合,遇到全字符串行时会重置为当前行的对应列内容。currentRowLeftHeaders:存储当前行的左侧多级表头,每次处理非全字符串行时更新。
- 行处理逻辑:
- 先判断是否为全表头行,是则重置上方表头;否则提取当前行的左侧表头。
- 仅处理数据对象,将左侧和上方表头的属性添加到对象中,最终收集到结果数组。
内容的提问来源于stack exchange,提问作者Lance Pollard
相关产品推荐
相关产品推荐

