前言
最近在掘金上看到了不少关于扁平数组转树形结构的文章,都是各式各样,实际工作中,我也有过类似的场景,只不过更奇葩一点 ~
背景
我是做一个可视化开发平台,基于拖拉拽快速实现可视化页面(PC, 手机,物理大屏)的低代码开发工具,在后续迭代开发中,我遇到了这样一个问题:
PC 页面已经开发 40% 左右进度了,客户新来需求,能不能一套代码兼容手机端 ? 我勒个去,什么鬼 ~ 说是新来需求,实则命令式要求,必须这么做 ! 作为工具平台研发的主要开发者,结果当然是默默的 “干”,由于平台本身就是通过拖拉拽生成配置文件,然后依赖平台,动态生成页面,这个配置文件中就有布局配置参数,当然是从这里下手啦(布局参数结构改变
),好了,距离主题越说越远,详细请查看我的另一篇文章 可视化平台实现思路
布局参数结构
[{
"x": 1, // x 轴位置
"y": 1, // y 轴位置
"w": 35, // 宽度比例
"h": 24, // 高度比例
"i": "1", // 唯一值
"title": "标题1" // 标题
}, {
"x": 2,
"y": 6,
"w": 19,
"h": 17,
"i": "2",
"title": "标题2"
}
........ 未知数量
];
常见的 ArrayToTree 案例
let data = [
{"parent_id": 0, "id":1, "value": "标题1"},
{"parent_id": 1, "id":3, "value": "标题2"},
{"parent_id": 4, "id":6, "value": "标题3"},
{"parent_id": 3, "id":5, "value": "标题4"},
{"parent_id": 2, "id":4, "value": "标题5"},
{"parent_id": 1, "id":2, "value": "标题6"},
]
// 方式1
function ArrayToTree(data){
let result = []
let dg_fun = (item) => {
let children = []
data.forEach((n,index) => {
if(n.parent_id == item.id){
children.push(n)
data.splice(index,1)
item["children"] = children
if(data.length) dg_fun(n)
}
})
}
data.forEach((c,index) => {
if(c.parent_id == 0){
result.push(c)
data.splice(index,1)
dg_fun(c)
}
})
return result;
}
// 方式2
function toTree(data) {
data.forEach(item => {
delete item.children;
})
const map = {};
data.forEach(item => {
map[item.id] = item;
})
const list = [];
data.forEach(item => {
let parent = map[item.parent_id];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
list.push(item)
}
})
return list;
}
上边两种方法,我就不过多介绍了
- 方式1,就是通过递归的方式,首先查找顶级节点,然后传入递归方法,依次递归查找子节点,找到则记录并移除,直到 data.length = 0
- 方式2,稍微好一些,建立映射表,一次遍历,直接去找父节点,找到对应父节点则把自己 push 进父节点的 children
我的实际案例
通过一张之前的测试开发视图来描述一下
如图所示
- 左边黄框是当前页面所用的组件列表,呈树状结构
这里就是扁平数组转tree后的结构
- 左边绿框是树状结构的多个根节点,分别包含着自己的多个子节点
- 右边红框则是组件视图,结合树状结构来看,1,2,3,4 块分别对应着左侧栏的组件管理的 1,2,3,4 ,每一块中的最大组件元素就是当前根节点元素
目的是要实现如下这样的需求,将Pc页面兼容在手机上查看,我就不过多描述后续的功能了,在这里主要分享ArrayToTree
转换过程
我的这个扁平数组其实是没有父子层级这些默认参数的,需要通过 x,y,w,h 四个参数来决定谁是谁的父节点,谁是谁的子节点,所以在判断上可能麻烦一些.x,y,w,h 注意分别代表什么,文章顶部布局参数结构有标注
话不多说,直接上代码,写来写去就只能写成这个样子,如童鞋们有好的方式,评论区吐槽一下,我好优化优化
window.Nt_tree_left_data = [{ "x": 0, "y": 0, "w": 10000, "h": 10000 }];
const FormatLeftMenuFun = function (s_copy) {
var s = JSON.parse(JSON.stringify(s_copy));
var max = [];
for (var j = 0; j < s.length; j++) {
var a = s[j];
var isMax = true;
// a 跟每个矩形比较,如果a在b中,说明 a不是最外层的矩形
for (var i = 0; i < s.length; i++) {
if (i == j) {
continue;
}
var b = s[i];
if ((a.x >= b.x && a.y >= b.y && a.x + a.w <= b.x + b.w && a.y + a.h <= b.y + b.h) &&
!(a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h)) {
isMax = false;
break;
}
}
// 是最外层的,就把 a 从数组中去除,插入到树节点上
if (isMax) {
//记录要删除的矩形
max.push(j);
//将a追加到树的节点上
insertNode2Tree(a, Nt_tree_left_data[0]);
}
}
var b2 = [];
//构建新的数组
for (var j = 0; j < s.length; j++) {
//没有被移除
if (max.indexOf(j) < 0) {
b2.push(s[j]);
}
}
if (b2.length > 0) {
FormatLeftMenuFun(b2);
}
return Nt_tree_left_data;
}
function insertNode2Tree (a, b) {
//a在b方框内
if ((a.x >= b.x && a.y >= b.y && a.x + a.w <= b.x + b.w && a.y + a.h <= b.y + b.h) &&
!(a.x == b.x && a.y == b.y && a.w == b.w && a.h == b.h)) {
//检查是否b有子集
for (var j = 0; b.children && j < b.children.length; j++) {
if (insertNode2Tree(a, b.children[j])) {
return true;
}
}
b.children = b.children ? b.children : [];
b.children.push(a);
return true;
}
return false;
}
export { FormatLeftMenuFun };
测试数据
// 上述方法需要用到
var s1=[{"x":1,"y":1,"w":35,"h":24,"i":"1","moved":false,"title":"标题1"},{"x":2,"y":6,"w":19,"h":17,"i":"2","moved":false,"title":"标题2"},{"x":37,"y":1,"w":22,"h":24,"i":"3","moved":false,"title":"标题3"},{"x":3,"y":7,"w":4,"h":2,"i":"4","moved":false,"title":"标题4"},{"x":22,"y":15,"w":12,"h":8,"i":"5","moved":false,"title":"标题5"},{"x":22,"y":6,"w":12,"h":8,"i":"6","moved":false,"title":"标题6"},{"x":2,"y":2,"w":10,"h":3,"i":"7","moved":false,"title":"标题7"},{"x":38,"y":5,"w":21,"h":19,"i":"8","moved":false,"title":"标题8"},{"x":38,"y":2,"w":6,"h":3,"i":"9","moved":false,"title":"标题9"},{"x":3,"y":9,"w":7,"h":3,"i":10,"moved":false,"title":"标题10"},{"x":14,"y":7,"w":6,"h":2,"i":11,"moved":false,"title":"标题11"},{"x":3,"y":15,"w":3,"h":2,"i":12,"moved":false,"title":"标题12"},{"x":3,"y":18,"w":4,"h":2,"i":13,"moved":false,"title":"标题13"},{"x":9,"y":15,"w":3,"h":2,"i":14,"moved":false,"title":"标题14"},{"x":15,"y":15,"w":3,"h":2,"i":15,"moved":false,"title":"标题15"},{"x":7,"y":15,"w":1,"h":5,"i":16,"moved":false,"title":"标题16"},{"x":13,"y":15,"w":1,"h":5,"i":17,"moved":false,"title":"标题17"},{"x":9,"y":18,"w":4,"h":2,"i":18,"moved":false,"title":"标题18"},{"x":1,"y":27,"w":24,"h":24,"i":19,"moved":false,"title":"标题19"},{"x":26,"y":27,"w":16,"h":24,"i":20,"moved":false,"title":"标题20"},{"x":43,"y":27,"w":16,"h":24,"i":21,"moved":false,"title":"标题21"},{"x":2,"y":28,"w":6,"h":3,"i":22,"moved":false,"title":"标题22"},{"x":12,"y":28,"w":13,"h":2,"i":23,"moved":false,"title":"标题23"},{"x":2,"y":31,"w":23,"h":19,"i":24,"moved":false,"title":"标题24"},{"x":27,"y":28,"w":7,"h":3,"i":25,"moved":false,"title":"标题25"},{"x":44,"y":32,"w":7,"h":17,"i":26,"moved":false,"title":"标题26"},{"x":44,"y":28,"w":7,"h":3,"i":27,"moved":false,"title":"标题27"},{"x":35,"y":32,"w":6,"h":17,"i":28,"moved":false,"title":"标题28"},{"x":36,"y":35,"w":4,"h":2,"i":29,"moved":false,"title":"标题29"},{"x":36,"y":37,"w":5,"h":2,"i":30,"moved":false,"title":"标题30"},{"x":27,"y":32,"w":7,"h":17,"i":31,"moved":false,"title":"标题31"},{"x":36,"y":41,"w":4,"h":2,"i":32,"moved":false,"title":"标题32"},{"x":36,"y":43,"w":5,"h":2,"i":33,"moved":false,"title":"标题33"},{"x":52,"y":32,"w":6,"h":17,"i":34,"moved":false,"title":"标题34"},{"x":53,"y":35,"w":4,"h":2,"i":35,"moved":false,"title":"标题35"},{"x":53,"y":41,"w":4,"h":2,"i":36,"moved":false,"title":"标题36"},{"x":53,"y":43,"w":5,"h":2,"i":37,"moved":false,"title":"标题37"},{"x":53,"y":37,"w":5,"h":2,"i":39,"moved":false,"title":"标题39"},{"x":1,"y":53,"w":18,"h":24,"i":40,"moved":false,"title":"标题40"},{"x":2,"y":54,"w":7,"h":3,"i":41,"moved":false,"title":"标题41"},{"x":20,"y":53,"w":39,"h":24,"i":42,"moved":false,"title":"标题42"},{"x":4,"y":58,"w":13,"h":18,"i":43,"moved":false,"title":"标题43"},{"x":21,"y":54,"w":9,"h":3,"i":44,"moved":false,"title":"标题44"},{"x":12,"y":54,"w":6,"h":3,"i":45,"moved":false,"title":"标题45"},{"x":21,"y":57,"w":30,"h":19,"i":46,"moved":false,"title":"标题46"},{"x":52,"y":57,"w":6,"h":19,"i":47,"moved":false,"title":"标题47"},{"x":53,"y":63,"w":5,"h":2,"i":48,"moved":false,"title":"标题48"},{"x":53,"y":61,"w":4,"h":2,"i":49,"moved":false,"title":"标题49"},{"x":53,"y":67,"w":4,"h":2,"i":50,"moved":false,"title":"标题50"},{"x":53,"y":69,"w":5,"h":2,"i":51,"moved":false,"title":"标题51"},{"x":1,"y":79,"w":58,"h":24,"i":52,"moved":false,"title":"标题52"},{"x":2,"y":80,"w":7,"h":3,"i":53,"moved":false,"title":"标题53"},{"x":2,"y":84,"w":46,"h":7,"i":54,"moved":false,"title":"标题54"},{"x":49,"y":84,"w":9,"h":15,"i":55,"moved":false,"title":"标题55"},{"x":2,"y":91,"w":22,"h":8,"i":56,"moved":false,"title":"标题56"},{"x":26,"y":91,"w":22,"h":8,"i":57,"moved":false,"title":"标题57"},{"x":50,"y":87,"w":4,"h":2,"i":58,"moved":false,"title":"标题58"},{"x":50,"y":85,"w":4,"h":2,"i":59,"moved":false,"title":"标题59"},{"x":52,"y":89,"w":3,"h":6,"i":60,"moved":false,"title":"标题60"},{"x":23,"y":7,"w":4,"h":2,"i":61,"moved":false,"title":"标题61"},{"x":23,"y":16,"w":4,"h":2,"i":62,"moved":false,"title":"标题62"},{"x":28,"y":11,"w":4,"h":2,"i":63,"moved":false,"title":"标题63"},{"x":23,"y":11,"w":4,"h":2,"i":64,"moved":false,"title":"标题64"},{"x":28,"y":9,"w":4,"h":2,"i":65,"moved":false,"title":"标题65"},{"x":28,"y":7,"w":4,"h":2,"i":66,"moved":false,"title":"标题66"},{"x":28,"y":20,"w":4,"h":2,"i":67,"moved":false,"title":"标题67"},{"x":15,"y":18,"w":4,"h":2,"i":68,"moved":false,"title":"标题68"},{"x":28,"y":18,"w":4,"h":2,"i":69,"moved":false,"title":"标题69"},{"x":23,"y":18,"w":4,"h":4,"i":70,"moved":false,"title":"标题70"},{"x":28,"y":16,"w":4,"h":2,"i":71,"moved":false,"title":"标题71"},{"x":1,"y":105,"w":58,"h":49,"i":72,"moved":false,"title":"标题72"},{"x":2,"y":107,"w":9,"h":3,"i":73,"moved":false,"title":"标题73"},{"x":2,"y":110,"w":5,"h":2,"i":74,"moved":false,"title":"标题74"},{"x":2,"y":112,"w":57,"h":42,"i":75,"moved":false,"title":"标题75"},{"x":26,"y":2,"w":4,"h":4,"i":76,"moved":false,"title":"标题76"}];
小小鼓励,大大成长,欢迎点赞,收藏