项目需要,要遍历以下JSON数据,以可读性好的方式展示给用户。
var request = {
"sheet": {
"一般体格检查及生命体征": [
{
"清点器械": true,
"自我介绍": true,
"观察一般状态": {},
"洗手消毒": true
},
{
"测体温": {
"status": true,
"duration": {
"value": 10,
"unit": "min"
}
},
"测血压": true,
// 至少30秒,注意两侧对比
"测脉搏": true,
"呼吸": true
}
],
"头颈部": [
{
"观察头部": {
"头部外形": "正常",
"毛发分布": "正常",
"异常运动": {}
},
"触诊头颅": {}
},
{
"视诊": {
"双眼": {},
"眉毛": {}
},
"检查": {
"下睑结膜": {},
"球结膜": {},
"巩膜": {}
},
"翻转上睑": {
"检查": {
"上睑结膜": {},
"球结膜": {},
"巩膜": {}
}
},
// 6 个方向
"眼球运动": ["左","左上","左下","右","右上","右下"],
"眼球震颤": false,
"对光反射": ["直接","间接"],
"集合反射": true
},
{
"视诊": ["外耳","外耳道"],
"触诊": ["耳廓","乳突"]
},
{
"视诊": ["外鼻","鼻前庭","鼻中隔"],
"触诊": ["外鼻"],
"压痛": ["上颌窦","额窦","筛窦"]
},
{
"视诊": ["口","唇","牙齿","上颚","舌质","舌苔"],
"借助压舌板观察": ["颊粘膜","牙齿","牙龈","口底"],
"口咽部": true,
"扁桃体": true
},
{
"视诊": {
"颈部": ["外形","皮肤","颈静脉充盈","颈动脉搏动"]
},
"颈部活动": true,
"触诊": {
"颈部淋巴结": ["耳前","耳后","枕后","颌下","颏下","颈前","颈后","锁骨上"],
"甲状腺": {},
"颈动脉": {},
"气管位置": {}
}
}
],
"前侧胸部": {
"肺部": {
"视诊": {
"胸廓": ["外形","对称性","皮肤"],
"呼吸": ["运动","频率","节律","深度"]
}
},
"心脏": {}
},
"背部(病人坐起)": {},
"腹部(病人躺下)": {},
"上肢": {},
"下肢": {},
"神经反射": {},
"步态与腰椎运动": {}
}
}
最终效果应该要类似下面这样子:
| 1 一般体格检查及生命体征
| | 1.1
| | | 1.1.1 清点器械
| | | | true
| | | 1.1.2 自我介绍
| | | | true
| | | 1.1.3 观察一般状态
| | | 1.1.4 洗手消毒
| | | | true
| | 1.2
| | | 1.2.1 测体温
| | | | 1.2.1.1 status
| | | | | true
| | | | 1.2.1.2 duration
| | | | | 1.2.1.2.1 value
| | | | | | 10
| | | | | 1.2.1.2.2 unit
| | | | | | min
| | | 1.2.2 测血压
| | | | true
| | | 1.2.3 测脉搏
| | | | true
| | | 1.2.4 呼吸
| | | | true
| 2 头颈部
| | 2.1
| | | 2.1.1 观察头部
| | | | 2.1.1.1 头部外形
| | | | | 正常
| | | | 2.1.1.2 毛发分布
| | | | | 正常
| | | | 2.1.1.3 异常运动
| | | 2.1.2 触诊头颅
| | 2.2
| | | 2.2.1 视诊
| | | | 2.2.1.1 双眼
| | | | 2.2.1.2 眉毛
| | | 2.2.2 检查
| | | | 2.2.2.1 下睑结膜
| | | | 2.2.2.2 球结膜
| | | | 2.2.2.3 巩膜
| | | 2.2.3 翻转上睑
| | | | 2.2.3.1 检查
| | | | | 2.2.3.1.1 上睑结膜
| | | | | 2.2.3.1.2 球结膜
| | | | | 2.2.3.1.3 巩膜
| | | 2.2.4 眼球运动
| | | | 2.2.4.1
| | | | | 左
| | | | 2.2.4.2
| | | | | 左上
| | | | 2.2.4.3
| | | | | 左下
| | | | 2.2.4.4
| | | | | 右
| | | | 2.2.4.5
| | | | | 右上
| | | | 2.2.4.6
| | | | | 右下
| | | 2.2.5 眼球震颤
| | | | false
| | | 2.2.6 对光反射
| | | | 2.2.6.1
| | | | | 直接
| | | | 2.2.6.2
| | | | | 间接
| | | 2.2.7 集合反射
| | | | true
| | 2.3
| | | 2.3.1 视诊
| | | | 2.3.1.1
| | | | | 外耳
| | | | 2.3.1.2
| | | | | 外耳道
| | | 2.3.2 触诊
| | | | 2.3.2.1
| | | | | 耳廓
| | | | 2.3.2.2
| | | | | 乳突
| | 2.4
| | | 2.4.1 视诊
| | | | 2.4.1.1
| | | | | 外鼻
| | | | 2.4.1.2
| | | | | 鼻前庭
| | | | 2.4.1.3
| | | | | 鼻中隔
| | | 2.4.2 触诊
| | | | 2.4.2.1
| | | | | 外鼻
| | | 2.4.3 压痛
| | | | 2.4.3.1
| | | | | 上颌窦
| | | | 2.4.3.2
| | | | | 额窦
| | | | 2.4.3.3
| | | | | 筛窦
| | 2.5
| | | 2.5.1 视诊
| | | | 2.5.1.1
| | | | | 口
| | | | 2.5.1.2
| | | | | 唇
| | | | 2.5.1.3
| | | | | 牙齿
| | | | 2.5.1.4
| | | | | 上颚
| | | | 2.5.1.5
| | | | | 舌质
| | | | 2.5.1.6
| | | | | 舌苔
| | | 2.5.2 借助压舌板观察
| | | | 2.5.2.1
| | | | | 颊粘膜
| | | | 2.5.2.2
| | | | | 牙齿
| | | | 2.5.2.3
| | | | | 牙龈
| | | | 2.5.2.4
| | | | | 口底
| | | 2.5.3 口咽部
| | | | true
| | | 2.5.4 扁桃体
| | | | true
| | 2.6
| | | 2.6.1 视诊
| | | | 2.6.1.1 颈部
| | | | | 2.6.1.1.1
| | | | | | 外形
| | | | | 2.6.1.1.2
| | | | | | 皮肤
| | | | | 2.6.1.1.3
| | | | | | 颈静脉充盈
| | | | | 2.6.1.1.4
| | | | | | 颈动脉搏动
| | | 2.6.2 颈部活动
| | | | true
| | | 2.6.3 触诊
| | | | 2.6.3.1 颈部淋巴结
| | | | | 2.6.3.1.1
| | | | | | 耳前
| | | | | 2.6.3.1.2
| | | | | | 耳后
| | | | | 2.6.3.1.3
| | | | | | 枕后
| | | | | 2.6.3.1.4
| | | | | | 颌下
| | | | | 2.6.3.1.5
| | | | | | 颏下
| | | | | 2.6.3.1.6
| | | | | | 颈前
| | | | | 2.6.3.1.7
| | | | | | 颈后
| | | | | 2.6.3.1.8
| | | | | | 锁骨上
| | | | 2.6.3.2 甲状腺
| | | | 2.6.3.3 颈动脉
| | | | 2.6.3.4 气管位置
| 3 前侧胸部
| | 3.1 肺部
| | | 3.1.1 视诊
| | | | 3.1.1.1 胸廓
| | | | | 3.1.1.1.1
| | | | | | 外形
| | | | | 3.1.1.1.2
| | | | | | 对称性
| | | | | 3.1.1.1.3
| | | | | | 皮肤
| | | | 3.1.1.2 呼吸
| | | | | 3.1.1.2.1
| | | | | | 运动
| | | | | 3.1.1.2.2
| | | | | | 频率
| | | | | 3.1.1.2.3
| | | | | | 节律
| | | | | 3.1.1.2.4
| | | | | | 深度
| | 3.2 心脏
| 4 背部(病人坐起)
| 5 腹部(病人躺下)
| 6 上肢
| 7 下肢
| 8 神经反射
| 9 步态与腰椎运动
可以看到,这种JSON对象极不规则,代码如何写得简洁在我脑中困难地凝练着。想起以前也有类似的痛苦经历,如何递归一层又一层的对象花费了我好几个小时。第二次接触这种项目当然也费了不少时间,好在最后理清了思路。嗯,这是目前看起来最优雅的代码了。
function exploit(JSONObject){
var layer = 1;
var layer_arr = new Array();
var pad = "| ";
reveal(JSONObject);
function reveal(JSONObject) {
var key_count = 0;
for ( key in JSONObject ) {
key_count ++
layer_arr.push(key_count);
var serial = pad.repeat(layer) + layer_arr.join('.') + " ";
if ( ! isNaN(parseInt(key)) ) {
console.log( serial );
} else {
console.log( serial + key );
}
layer ++
if ( typeof(JSONObject[key]) == 'object' & JSONObject[key] != {} ) {
var previous_count = key_count;
reveal(JSONObject[key]);
key_count = previous_count;
} else {
console.log( pad.repeat( layer ) + JSONObject[key] );
}
layer --
layer_arr.pop();
}
}
}
exploit(request['sheet']);
输出已经在上面的最终效果那里给出,就不赘述了。
总结一下这类问题的关键点:
1. 函数调用自身。如上面的reveal函数在合适的时候调用了自己,每次只剥开一层,但能递归地剥开下一层,因此能遍历整个JSON。
2. 灵活运用局部变量和全局变量。经过反复思考,我把key_count作为局部变量,在每次调用reveal函数,也就是进入下一层遍历的时候都能归零,也就是重新进行编号;在进入下一层之前,使用一个最局部的变量previous_count保存遍历的进度信息;layer和layer_arr是遍历过程中遍历深度的记录信息,放在函数外作为全局变量;pad是缩进所用的字符串,不需要随着函数的调用而变化,因此也作为全局变量。这些变量在代码中的位置稍有偏差就不能达到想要的效果,可谓是“失之毫厘,谬以千里”。
呼~终于能用更短的代码实现这一功能了。个人感觉这个函数的扩展性很大,在我搜索的文章中似乎还没有人实现过这样的功能(不然我为什么要忍痛自己写呢?),想跟大家分享一下。如果大家还有什么更好的办法,或者已经有函数实现了上述功能,欢迎在评论区留言!
最后,我给我的实现方法起了个名字——“剥空心菜”,是因为启示我这样做的是初中世界历史课上做题遇到的一句话: