数组转Tree还是这么“卷”

2,228 阅读8分钟

前言

最近在掘金上看到了不少关于扁平数组转树形结构的文章,都是各式各样,实际工作中,我也有过类似的场景,只不过更奇葩一点 ~

背景

我是做一个可视化开发平台,基于拖拉拽快速实现可视化页面(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

我的实际案例

通过一张之前的测试开发视图来描述一下

1638847955.png

如图所示

  • 左边黄框是当前页面所用的组件列表,呈树状结构 这里就是扁平数组转tree后的结构
  • 左边绿框是树状结构的多个根节点,分别包含着自己的多个子节点
  • 右边红框则是组件视图,结合树状结构来看,1,2,3,4 块分别对应着左侧栏的组件管理的 1,2,3,4 ,每一块中的最大组件元素就是当前根节点元素

目的是要实现如下这样的需求,将Pc页面兼容在手机上查看,我就不过多描述后续的功能了,在这里主要分享ArrayToTree

1638847984.png

转换过程

我的这个扁平数组其实是没有父子层级这些默认参数的,需要通过 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"}];

小小鼓励,大大成长,欢迎点赞,收藏