在思维简图中,软件可以将用户录入的文本直接转化为思维导图。其规则是:用户录入的文字必须以一个感叹号作为一个节点的起始标记,一个感叹号表示后面的文字处于第一级节点,两个感叹号表示后面的文字处于第二个节点,以此类推。
!内容
!!同盟会的政治纲领是“驱除鞑虏,恢复中华,创立民国,平均地权”。
!!孙中山将同盟会的纲领概括为三大主义,即民族主义、民权主义、民生主义,后被称为三民主义。
!!!民族主义,即民族革命,包括“驱除鞑虏,恢复中华”两项内容。
!!!民权主义即政治革命,内容是“创立民国”,即建立资产阶级民主共和国。
!!!民生主义即社会革命,指的是“平均地权”。
!评价:
!!它初步描绘出中国还不曾有过的资产阶级共和国方案,是一个比较完整而明确的资产阶级民主革命纲领。
!!对推动革命的发展产生了重大而积极的影响。
!!但它并不是一个彻底的资产阶级民主革命纲领。
所以, 上面这段文字经过软件处理后应该生成的树状图应该如下:
在将文字转化为图片的过程中,如何将录入文本转化为一个结构化的数据至关重要。按照最容易想到的思路,算法设计应该是这样:
在全文的最末尾加一个感叹号,然后找出所有处于单个感叹号之间的文字,放入一个数组中;
将数组中的第一个节点的文字提取出来,作为这个节点的title值保存下来,并且保存自己的父级的id(对于第一级的节点,父级节点id取-1),创建一个自己的id;
上述处理完毕之后,用递归的方式处理第二级、第三级……节点,直到全部遍历完。
这就是我写第一版程序时的思路,这个算法写得很笨,也写得很痛苦。层层递归的方式,在程序调试时不那么符合地球人的思维习惯。那种感觉很像是我们诟病Nodejs里的层层回调嵌套出现的“死亡金字塔”式的程序结构。
前些天阅读过promise编程模式,很好的破解掉了这个“死亡金字塔”,让代码的结构非常便于阅读和调试。受到这个启发,再次思考了这个算法的设计,应该有一个调试起来思路更清楚,代码结构更一目了然的方式:
首先不考虑感叹号的多少,把感叹号之间的文字提取出来,放入一个数组;
遍历数组,将数组中每个元素创建为一个节点实例,实例包含如下属性:id, title, level, parent, childrenids。id即自己在数组中的序号,title为自身文字,level即自身文字中包含感叹号的数量。parent的算法稍复杂:寻找id和自己最接近且level比自己小1的节点,它的id即parent的值。childrenids暂时设为一个空数组。
上一步遍历完成后,再次遍历,根据parent值将各个节点的childrenids数组填写好。
至此,所有节点的属性值已经都具备。如果我们需要用一个json来结构化的表达这一组数据,那么只需要再次遍历数组,创建一个json,将相应的值填充好就可以了。
下面是实现这个算法的代码:
var str="!内容 !!同盟会的政治纲领是“驱除鞑虏,恢复中华,创立民国,平均地权”。 !!孙中山将同盟会的纲领概括为三大主义,即民族主义、民权主义、民生主义,后被称为三民主义。 !!!民族主义,即民族革命,包括“驱除鞑虏,恢复中华”两项内容。 !!!民权主义即政治革命,内容是“创立民国”,即建立资产阶级民主共和国。 !!!民生主义即社会革命,指的是“平均地权”。 !评价: !!它初步描绘出中国还不曾有过的资产阶级共和国方案,是一个比较完整而明确的资产阶级民主革命纲领。!!对推动革命的发展产生了重大而积极的影响。 !!但它并不是一个彻底的资产阶级民主革命纲领。"; //提取文字创建数组 var pt=/!+[^!]+(?=!)/g; var arr=str.match(pt); console.log(arr) //遍历数组,将数组中每个元素创建为一个节点实例 var nodes=[]; for(var i in arr){ var obj={}; obj.id=i; var len=arr[i].length; obj.title=arr[i].replace(/!/g,""); obj.level=len-obj.title.length; if(obj.level==1)obj.parent=-1; else obj.parent=findparent(obj.level,obj.id).id; obj.childrenids=[]; nodes.push(obj); } //遍历数组,为每个节点补上子节点集合 for(var i in nodes){ nodes[i].childrenids=findchildrenid(nodes[i].id); } //转化为json数组 var json={ title:"三民主义学说和资产阶级共和国方案", id:-1, parent:-1, level:-1, childrenids:findchildrenid(-1) } createjson(json,json.childrenids); function createjson(node,idarr){ var c=[]; for(var i in idarr){ c.push(findbyid(idarr[i])); } node.children=c; for(var i in c){ createjson(c[i],c[i].childrenids); } } console.log(JSON.stringify(json,null,"/t")) function findchildrenid(id){ var arr=[]; for(var i in nodes){ if(nodes[i].parent==id)arr.push(nodes[i].id); } return arr; } function findparent(level,id){ var parr=findbylevel(level-1); var n={},sub; for(var i in parr){ if(parr[i].id<id){ if(i==0 || id-parr[i].id<sub){ sub=id-parr[i].id; n=parr[i]; } } } return n; } function findbylevel(level){ var arr=[]; for(var i in nodes){ if(nodes[i].level==level)arr.push(nodes[i]); } return arr; } function findbyid(id){ for(var i in nodes)if(nodes[i].id==id)return nodes[i]; return {}; }
最后执行生成json的打印结果如下:
{ "title": "三民主义学说和资产阶级共和国方案", "id": -1, "parent": -1, "level": -1, "childrenids": [ "0", "6" ], "children": [ { "id": "0", "title": "内容 ", "level": 1, "parent": -1, "childrenids": [ "1", "2" ], "children": [ { "id": "1", "title": "同盟会的政治纲领是“驱除鞑虏,恢复中华,创立民国,平均地权”。 ", "level": 2, "parent": "0", "childrenids": [], "children": [] }, { "id": "2", "title": "孙中山将同盟会的纲领概括为三大主义,即民族主义、民权主义、民生主义,后被称为三民主义。 ", "level": 2, "parent": "0", "childrenids": [ "3", "4", "5" ], "children": [ { "id": "3", "title": "民族主义,即民族革命,包括“驱除鞑虏,恢复中华”两项内容。 ", "level": 3, "parent": "2", "childrenids": [], "children": [] }, { "id": "4", "title": "民权主义即政治革命,内容是“创立民国”,即建立资产阶级民主共和国。 ", "level": 3, "parent": "2", "childrenids": [], "children": [] }, { "id": "5", "title": "民生主义即社会革命,指的是“平均地权”。 ", "level": 3, "parent": "2", "childrenids": [], "children": [] } ] } ] }, { "id": "6", "title": "评价: ", "level": 1, "parent": -1, "childrenids": [ "7", "8" ], "children": [ { "id": "7", "title": "它初步描绘出中国还不曾有过的资产阶级共和国方案,是一个比较完整而明确的资产阶级民主革命纲领。", "level": 2, "parent": "6", "childrenids": [], "children": [] }, { "id": "8", "title": "对推动革命的发展产生了重大而积极的影响。 ", "level": 2, "parent": "6", "childrenids": [], "children": [] } ] } ] }