最新消息:看到那些跳动的图片、文字了吗?点击点击 O(∩_∩)O~~

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

若思若想 onlyling 3384浏览

背景

工作业务需要做一个下图这样的深层的数据选择弹窗,但是后端给数据的时候,不好拼数据,只好给一个扁平的,带父级关系的数组对象。入下面这样的数据格式,没给节点都有他对应的 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里基本类型(值)和复杂类型(引用)有什么区别?

其他的应该就没有了吧。

转载请注明:OnlyLing - Web 前端开发者 » 扁平关系数据结构转换为深层关系数据结构