背景
工作业务需要做一个下图这样的深层的数据选择弹窗,但是后端给数据的时候,不好拼数据,只好给一个扁平的,带父级关系的数组对象。入下面这样的数据格式,没给节点都有他对应的 superChnlId
,实际我们需要后面那种关系数据结构。
{
"messageBody": [
{
"chnlId": "1",
"chnlCode": "322123",
"chnlLevel": "1",
"chnlName": "头结点",
"superChnlId": "0"
},
{
"chnlId": "2",
"chnlCode": "32121",
"chnlLevel": "2",
"chnlName": "第一层-哈哈哈",
"superChnlId": "1"
},
{
"chnlId": "3",
"chnlCode": "35435",
"chnlLevel": "2",
"chnlName": "第一层-gg发",
"superChnlId": "1"
},
{
"chnlId": "4",
"chnlCode": "435436",
"chnlLevel": "2",
"chnlName": "第一层-好肉",
"superChnlId": "1"
},
{
"chnlId": "5",
"chnlCode": "5765756",
"chnlLevel": "3",
"chnlName": "第二层-天山",
"superChnlId": "2"
},
{
"chnlId": "6",
"chnlCode": "5676657",
"chnlLevel": "3",
"chnlName": "第二层-啦玛",
"superChnlId": "2"
},
{
"chnlId": "7",
"chnlCode": "657652",
"chnlLevel": "4",
"chnlName": "第三层-第一营业厅",
"superChnlId": "5"
},
{
"chnlId": "8",
"chnlCode": "678673",
"chnlLevel": "4",
"chnlName": "第三层-第二营业厅",
"superChnlId": "5"
}
]
}
[
{
"chnlId": "1",
"chnlCode": "322123",
"chnlLevel": "1",
"chnlName": "头结点",
"superChnlId": "0",
"sub": [
{
"chnlId": "2",
"chnlCode": "32121",
"chnlLevel": "2",
"chnlName": "第一层-哈哈哈",
"superChnlId": "1",
"sub": [
{
"chnlId": "5",
"chnlCode": "5765756",
"chnlLevel": "3",
"chnlName": "第二层-天山",
"superChnlId": "2",
"sub": [
{
"chnlId": "7",
"chnlCode": "657652",
"chnlLevel": "4",
"chnlName": "第三层-第一营业厅",
"superChnlId": "5",
"sub": []
},
{
"chnlId": "8",
"chnlCode": "678673",
"chnlLevel": "4",
"chnlName": "第三层-第二营业厅",
"superChnlId": "5",
"sub": []
}
]
},
{
"chnlId": "6",
"chnlCode": "5676657",
"chnlLevel": "3",
"chnlName": "第二层-啦玛",
"superChnlId": "2",
"sub": []
}
]
},
{
"chnlId": "3",
"chnlCode": "35435",
"chnlLevel": "2",
"chnlName": "第一层-gg发",
"superChnlId": "1",
"sub": []
},
{
"chnlId": "4",
"chnlCode": "435436",
"chnlLevel": "2",
"chnlName": "第一层-好肉",
"superChnlId": "1",
"sub": []
}
]
}
]
不要怂就是干
目前数据关系上,可以确定的是,可以通过 superChnlId
, 找到上级,superChnlId
为 0
的时候,它是顶级节点。
我们先定义一个空数组 CRM_TREE
,用来标识最后需要的关系数组
// _data 为原始数据 messageBody
$.each(_data, function (index, obj) {
if (obj.superChnlId == '0') {
// 顶级菜单
CRM_TREE.push(_obj)
} else {
}
})
接下来,问题来了,如何才能知道当前节点的父节点在哪儿,或者说我们应该怎么把当前节点放入它的父节点的 sub
中呢。
var _node = CRM_TREE[0].sub[n].sub[n]...
// 直到某个具体的节点
_node.push(_obj)
难点大概就是如何找到具体的某个节点吧 -、-
我们知道父节点的访问路径,也就解决了?试试看。
定义一个路径对象 parentMap
,用于存取父节点的访问路径。因为我们是一层一层的去找,具体路径会是一个数组。
// 假设父节点为7的路径
var parentMap = {
'7': [0, '2', '5', '7']
}
// chnlId=='xx' 假设通过一个方法找到 chnlId为xx的对象
CRM_TREE[0].sub[(chnlId=='2')].sub[chnlId=='5'].sub[chnlId=='7']
好像思路有了,很不错的样子。由于顶级节点是通过数组下标查找的,于是,又要多一个变量记录当前是哪个下标。
var parentMap = {}
var treeIndex = 0
$.each(_data, function (index, obj) {
if (obj.superChnlId == '0') {
// 顶级菜单
CRM_TREE.push(_obj)
// 记录当前路径
parentMap[obj.chnlId] = [treeIndex++]
} else {
// 父节点的路径
var path = parentMap[obj.superChnlId]
// 找到当前的父节点
var _parent = findParent(path)
// 复制一份父节点路径
var _path = getArray(path)
// 把当前节点ID加入,当前节点路径
_path.push(obj.chnlId)
// 添加到数据结构中
_parent.sub.push(_obj)
// 完善节点路径
parentMap[obj.chnlId] = _path
}
})
重点来了,我们来找父节点
// 工具方法 复制一个数组
function getArray(arr) {
return arr.length ? [].concat(arr) : []
}
// 工具方法 从给一个简单数组中删除某个值
function simpleArrayRemoveValue(list, el) {
var index;
for (var i = list.length - 1; i >= 0; i--) {
if (list[i] == el) {
index = i
}
}
return list.slice(0, index).concat(list.splice(index + 1))
}
// 通了路径找到父节点
// 因为顶级节点比较特殊,单独处理
// 以后的节点都是相似的,遍历路径找到
// 这里运用到了"传址"的特性,实现改变一个对象的,原来的对象也改变
function findParent(path) {
var _path = getArray(path)
var _index = _path[0]
var _path = simpleArrayRemoveValue(_path, _index)
var __current = CRM_TREE[_index]
for (var i = 0; i < _path.length; i++) {
__current = listFindObj(__current.sub, _path[i])
}
return __current
}
// 从一个列表中找到对应id的对象
function listFindObj(arr, id) {
var _obj = {}
$.each(arr, function (index, obj) {
if (obj.chnlId == id) {
_obj = obj
return false
}
})
return _obj
}
连招
// 工具方法 复制一个数组
function getArray(arr) {
return arr.length ? [].concat(arr) : []
}
// 工具方法 从给一个简单数组中删除某个值
function simpleArrayRemoveValue(list, el) {
var index;
for (var i = list.length - 1; i >= 0; i--) {
if (list[i] == el) {
index = i
}
}
return list.slice(0, index).concat(list.splice(index + 1))
}
// 通了路径找到父节点
function findParent(path) {
var _path = getArray(path)
var _index = _path[0]
var _path = simpleArrayRemoveValue(_path, _index)
var __current = CRM_TREE[_index]
for (var i = 0; i < _path.length; i++) {
__current = listFindObj(__current.sub, _path[i])
}
return __current
}
// 从一个列表中找到对应id的对象
function listFindObj(arr, id) {
var _obj = {}
$.each(arr, function (index, obj) {
if (obj.chnlId == id) {
_obj = obj
return false
}
})
return _obj
}
var CRM_TREE = []
var parentMap = {}
var treeIndex = 0
$.each(_data, function (index, obj) {
if (obj.superChnlId == '0') {
CRM_TREE.push(_obj)
parentMap[obj.chnlId] = [treeIndex++]
} else {
var path = parentMap[obj.superChnlId]
var _parent = findParent(path)
var _path = getArray(path)
_path.push(obj.chnlId)
_parent.sub.push(_obj)
parentMap[obj.chnlId] = _path
}
})
console.log(CRM_TREE)
这里可能运用最多,也可能不太理解的就是 js 中复杂类型(引用),JS里基本类型(值)和复杂类型(引用)有什么区别?
其他的应该就没有了吧。