扁平关系数据结构转换为深层关系数据结构

3,633次阅读
没有评论

共计 4403 个字符,预计需要花费 12 分钟才能阅读完成。

背景

工作业务需要做一个下图这样的深层的数据选择弹窗,但是后端给数据的时候,不好拼数据,只好给一个扁平的,带父级关系的数组对象。入下面这样的数据格式,没给节点都有他对应的 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, 找到上级,superChnlId0 的时候,它是顶级节点。

我们先定义一个空数组 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 里基本类型(值)和复杂类型(引用)有什么区别?

其他的应该就没有了吧。

正文完
 0
评论(没有评论)
验证码