# JavaScript 获取列表元素深度,以及对应匹配索引
曾在一个Pyhtmd (opens new window)中处理 HTML List 标签的解析问题。
# 问题:如何解析嵌套多层 HTML list 标签
假设: <ul>
= [
,</ul>
= ]
, <ol>
的问题就不展开了。
# 分析:
简化后,通过移除非数组的元素,我们得到下面的结构比较:
[1,2,3,[5,6]] => [[]]
[1,[2,[3,[]]]] => [[[[]]]]
...
我们通过这个结构 [[[[]]]]
,认为是一个字符串 '[[[[]]]]'
。
为了更形象的描述 [
始终对应 ]
构成一个 组合。
如下:
形象化: "a,b,c,d d1,c1,b1,a1"
对应的 | | | | | | | |
原始字符 [,[,[,[ ], ], ], ]
序号: 0 1 2 3 4 5 6 7
于是得到:
a index = 0
b index = 1
c index = 2
d index = 3
d1 index = 4
c1 index = 5
b1 index = 6
a1 index = 7
得出并收集它们的索引值关系:
0 1, 2, 3
↓ ↓ ↓ ↓
4 5 6 7
经过一个 level 提取算法 (opens new window) 的实现。
这个公式是经过长时间的观察、反复推测后,得到了一个有趣的现象:
公式:当前 index
x 2 - value
= 对应的 level
,这个公式非常的关键。
我们对 [0,1,2,3]
,通过算法转换如下:
index | 0 | 1 | 2 | 3 |
---|---|---|---|---|
value | 0 | 1 | 2 | 3 |
index *2 - value | 0*2-0=0 | 1*2-1=1 | 2*2-2=2 | 3*2-3=3 |
或许有人说,这个公式可能是个巧合,不妨看一下下面的:
已知,我们的结构是 [[[][]]]
,此时,左包围是对应索引与右括号的关系是 ,
0 1, 2, 4
↓ ↓ ↓ ↓
7 2 6 5
根据公式转换如下:
index | 0 | 1 | 2 | 4 |
---|---|---|---|---|
value | 0 | 1 | 2 | 3 |
index *2 - value | 0*2-0=0 | 1*2-1=1 | 2*2-2=2 | 3*2-4=2 |
直到这里,我们得出一个结论,只要知道左边的索引值,就可以对应的找到它合并为一个组合所在的索引值(即左边 [
,找到匹配右边结束的 ]
),以及它对应的 level。
这个算法被我应用于一款 HTML 转 Markdown 工具 Pyhtmd (opens new window),作为解析 HTML 列表元素 为 markdown 代码的核心支撑。
现在,如果得到最大 level 的深度,只需 Math.max.apply(this,[0,1,2,2])
即可。
下面是完整的代码实现(仅返回 level
最大级):
const arrayDeepLevel = function(array) {
const arrayStr = JSON.stringify(array);
let clearArrayStr = "";
// 这里的正则没有写好
arrayStr.replace(/(?:(\[|\]))/g, function($1) {
clearArrayStr += $1;
return $1;
});
const leftStrItems = [];
// const rightStrItems = [];
for (let i = 0; i < clearArrayStr.length; i++) {
// start tag
if (clearArrayStr[i] === "[") {
leftStrItems.push(i);
}
// ellipsis right Items ...
}
const leftMaps = {};
for (let i = 0; i < leftStrItems.length; i++) {
const item = leftStrItems[i];
leftMaps[item] = i * 2 - item;
}
const levelArray = Object.values(leftMaps);
return Math.max.apply(this, levelArray);
};
module.exports = arrayDeepLevel;
it("4. ==> level:2", () => {
// 实际上这里最高二级,是针对外包围计算,而非元素
expect(arrayDeepLevel([0, [1], [1, [2]]])).toBe(2);
});
# 总结
尽量简化复杂的问题
有时可能需要一双慧眼,以及反复的观测和总结