前端必备:高效处理树形数据与数组的实用函数_数组

🌈个人主页:前端青山 🔥系列专栏:Vue篇
🔖人终将被年少不可得之物困其一生

依旧青山,本期给大家带来Vuet篇专栏内容:Vue-树形数据处理|数组:实用函数封装

大家好,依旧青山,在开发项目过程中,前端对于数据的处理往往会封装一些公共的函数来提高工作效率和复用性,那么我给大家列几个数据处理函数封装

目录

数组添加键值对

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

数组转换为树结构

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

合并两数组转树形结构

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

数组列值组合字符串

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

树形结构查找节点

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

单条数据权限验证

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

树转列表

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

深拷贝对象|数组

功能概述

接收参数:

功能描述:

示例:

注意事项:

返回值:

总结

数组添加键值对

  • 给数组里面的每个object元素添加新的key值,
export function addKeysToData(arr, newKeys, froms) {
    if (Array.isArray(arr)) {
        arr.map((ele) => {
            for (let i = 0; i < newKeys.length; i++) {
                Vue.set(ele, newKeys[i], ele[froms[i]])
            }
            return ele
        })
    }
    return arr
}

功能概述

接收参数:
  • arr: 需要修改的数据数组。
  • newKeys: 要添加的新键名数组。
  • froms: 原数据中对应值的键名数组。
功能描述:
  1. 遍历数组: 遍历arr中的每个对象。
  2. 添加新键值对: 对于每个对象,根据newKeysfroms数组中的对应索引,从对象中获取值并添加新的键值对。
  3. 使用Vue.set方法: 使用Vue的Vue.set方法动态添加新键值对,确保响应式更新。
示例:
  • 原始数组:
[{"id":1,"name":"张三"},{"id":2,"name":"李四"}]
  • 调用函数:
addKeysToData(arr, ["userId","userName"], ["id","name"])
  • 返回结果:
  • 结果将是:
javascript[
  {"id":1,"name":"张三","userId":1,"userName":"张三"},
  {"id":2,"name":"李四","userId":2,"userName":"李四"}
]
注意事项:
  • 使用Vue.set方法是为了确保Vue能够检测到新添加的属性,从而触发视图更新。
  • 如果不需要Vue的响应式特性,可以使用Object.defineProperty方法替代Vue.set
返回值:
  • 返回修改后的数组arr

数组转换为树结构

将数组根据key值转换为有层级的树结构数组,必须有id属性

export function arrayToTree(arr, key = 'parent_id', needSort = true, type = 'id') {
    let tree = [] //最后生成的树列表


    当为公司列表数据时,对本部数据进行处理,因为id有重复,则将带有本部的数据id加上'-1'
    for (let i = 0; i < arr.length; i++) {
        if (arr[i].name && (arr[i].name.indexOf("(本部)") != -1 || arr[i].name.indexOf("(本部)") != -1)) {
            arr[i].id += "-1";
        }
    }
    //查找一级节点
    let deleteIndexArr = []
    for (let i = 0; i < arr.length; i++) {
        let ele = arr[i]
        //如果是一节节点,直接插入tree列表
        if (!ele[key]) {
            tree.push(ele)
            deleteIndexArr.push(i)
        } else {
            let parentExist = false
            for (let j = 0; j < arr.length; j++) {
                if (ele[key] == arr[j].id) {

                    parentExist = true
                    break
                }
            }
            //如果在数组中不存在父节点,则在当前树中为1及节点
            if (!parentExist) {
                tree.push(ele)
                deleteIndexArr.push(i)
            }
        }
    }
    //在列表中删除一级节点
    for (let i = deleteIndexArr.length - 1; i >= 0; i--) {
        let index = deleteIndexArr[i]
        arr.splice(index, 1)
    }
    //使用完后,设置为空
    deleteIndexArr = null

    //循环遍历建树,直接数组为空,为防止数据错误导致死循环,暂时循环10次后退出
    let times = 0
    while (arr.length > 0 && times < 10) {
        times++
        let len = arr.length
        for (let i = 0; i < len; i++) {
            let ele = arr[i]

            let parentItem = findParent(tree, ele, key)
            //如果找到其父节点
            if (parentItem) {
                //如父节点已有子列表,则将当前节点插入父节点的childrenlist中
                if (parentItem.childrenList) {
                    parentItem.childrenList.push(ele)
                }
                //如果还没有子列表,创建childrenList,并将元素插入其中
                else {
                    if (!parentItem.children) {
                        Vue.set(parentItem, "children", 1);
                    }

                    Vue.set(parentItem, "childrenList", []);
                    parentItem.childrenList.push(ele);
                }
                arr.splice(i, 1)
                len--
                i--
            }

        }
    }

    //将找不到父节点的数据直接加入一级节点
    if (arr.length > 0) {
        tree.push(...arr)
    }

    function sort(eles){
        eles.sort((a, b) => a[type] < b[type] ? -1 : 1)
        eles.forEach(val => {
            if(val.childrenList && val.childrenList.length > 0){
                sort(val.childrenList)
            }
        })
    }

    if (needSort) {
        sort(tree)
    }

    return tree
}

功能概述

接收参数:
  • arr: 需要转换为树形结构的原始数组。
  • key: 指定作为父节点ID的属性名,默认为"parent_id"
  • needSort: 是否需要对树进行排序,默认为true
  • type: 排序依据的属性名,默认为"id"
功能描述:
  1. 处理特殊数据: 如果数组中的某个元素的name属性包含"(本部)"或"(本部)",则在其id属性后加上"-1"
  2. 查找一级节点: 找出没有父节点的元素,即一级节点,并将它们添加到树形结构中。
  3. 构建树形结构: 循环遍历数组,为每个元素找到其父节点,并将其添加到父节点的子节点列表中。
  4. 处理剩余元素: 如果循环结束后仍有元素未被添加到树中,则将这些元素视为一级节点添加到树中。
  5. 排序树形结构: 如果needSorttrue,则对整个树形结构进行排序。
示例:
  • 原始数组:
[{"id":1,"name":"部门A"},{"id":2,"name":"部门B","parent_id":1},{"id":3,"name":"小组X","parent_id":2},{"id":4,"name":"小组Y","parent_id":1}]
  • 调用函数:
arrayToTree(arr)
  • 返回结果:
  • 结果将是一个树形结构,类似于:
javascript[
  {"id":1,"name":"部门A","childrenList":[{"id":2,"name":"部门B"},{"id":4,"name":"小组Y"}]},
  {"id":2,"name":"部门B","childrenList":[{"id":3,"name":"小组X"}]}
]
注意事项:
  • 函数假设输入的数组中的元素具有正确的parent_id信息,以构建出正确的树形结构。
  • 如果needSorttrue,则会对树形结构进行排序,排序依据为type属性。
返回值:
  • 返回一个树形结构的数组,其中包含了原始数组中的所有数据,并且按照层级关系组织。

合并两数组转树形结构

将两个数组合并成一个数组,并根据key值转换为有层级的树结构数组,必须有id属性,两个数组分别来自不同数据源,因此ID可能有重复,第二个数组仅只能包含最末级子节点

export function twoArrayToTree(arr1, arr2, key = "parent_id") {
    //先处理第二个数据,给id加上唯一标识
    for (let i = 0; i < arr2.length; i++) {
        arr2[i].id = 'c' + arr2[i].id
    }
    let arr = []; //临时数组,合并arr1和arr2
    arr.push(...arr1);
    //对父节点进行排序
    arr.sort((a, b) => {
        return a.id < b.id ? 1 : -1
    });
    //子节点不排序并反序后插入,目的是为了最终的树按照原先的顺序返回,
    arr.push(...arr2.reverse());
    let tree = arrayToTree(arr, key, false); //最后生成的树列表

    //去除掉第二个数组id前的标识
    for (let i = 0; i < arr2.length; i++) {
        arr2[i].id = arr2[i].id.substring(1)
    }
    return tree;
}

功能概述

接收参数:
  • arr1: 第一个数据数组,通常包含根节点或顶级节点。
  • arr2: 第二个数据数组,通常包含子节点或其他层级的节点。
  • key: 指定作为父节点ID的属性名,默认为"parent_id"
功能描述:
  1. 处理第二个数组: 给arr2中的每个对象的id属性前加上"c"作为唯一标识。
  2. 合并数组: 将arr1arr2合并到一个新的数组arr中。
  3. 排序父节点: 对合并后的数组arr中的父节点按id进行降序排序。
  4. 插入子节点: 将arr2反向排序后插入到arr中。这样可以确保子节点按照原始顺序被正确地插入到父节点下。
  5. 转换为树结构: 使用arrayToTree函数将合并后的数组转换成树形结构。
  6. 移除标识: 最后移除arr2中每个对象id前的"c"标识。
示例:
  • 原始数组:
  • arr1:
[{"id":1,"name":"部门A"},{"id":2,"name":"部门B"}]
  • arr2:
[{"id":3,"name":"小组X","parent_id":1},{"id":4,"name":"小组Y","parent_id":2}]
  • 调用函数:
twoArrayToTree(arr1, arr2)
  • 返回结果:
  • 结果将是一个树形结构,类似于:
javascript[
  {"id":1,"name":"部门A","children":[{"id":"c3","name":"小组X"}]},
  {"id":2,"name":"部门B","children":[{"id":"c4","name":"小组Y"}]}
]
注意事项:
  • 函数假设输入的数组已经包含了正确的parent_id信息,以构建出正确的树形结构。
  • arrayToTree函数未在提供的代码片段中给出,需要确保它能够正确地将数组转换成树形结构。
返回值:
  • 返回一个树形结构的数组,其中包含了arr1arr2中的所有数据,并且按照层级关系组织。

数组列值组合字符串

获取数组对应列合并后的字符串,字符串用splitStr分割

export function getArrayColStrs(arr, colKey = 'id', splitStr = ',') {
    if (!arr || arr.length == 0) return '';

    return arr.map(e => e[colKey]).join(splitStr)
}

功能概述

接收参数:
  • arr: 需要处理的数组。
  • colKey: 指定从数组元素中提取的键名,默认为"id"
  • splitStr: 用于连接提取的值的分隔符,默认为,
功能描述:
  1. 检查数组: 如果arr为空或长度为0,则返回空字符串。
  2. 提取键值: 从数组中的每个元素中提取指定键colKey对应的值。
  3. 拼接字符串: 使用splitStr将提取的值连接成一个字符串。
示例:
  • 原始数组:
[{"id":1,"name":"张三"},{"id":2,"name":"李四"}]
  • 调用函数:
getArrayColStrs(arr, "id", ",")
  • 返回结果:
  • 结果将是:
plaintext

"1,2"
注意事项:
  • 如果数组中的元素缺少指定的键colKey,则相应的值将为undefined
返回值:
  • 返回一个字符串,其中包含了数组中所有元素的指定键值,使用splitStr分隔。

树形结构查找节点

在list数组中,根据id查找对应的节点

export function getNode(list, id) {
    let node = null;
    for (let i = 0; i < list.length; i++) {
        if (list[i].id == id) {
            node = list[i];
            break;
        }
        if (list[i].childrenList && list[i].childrenList.length > 0) {
            let item = getNode(list[i].childrenList, id);
            if (item != null) {
                node = item;
                break;
            }
        }
    }
    return node;
}

功能概述

接收参数:
  • list: 需要搜索的树形结构数组。
  • id: 需要查找的节点的ID。
功能描述:
  1. 遍历树: 从list开始,递归地遍历树形结构。
  2. 查找节点: 查找具有指定id的节点。
  3. 返回节点: 如果找到匹配的节点,则返回该节点;否则返回null
示例:
  • 树形结构:
javascript[
  {"id":1,"name":"部门A","childrenList":[{"id":2,"name":"部门B"},{"id":4,"name":"小组Y"}]},
  {"id":2,"name":"部门B","childrenList":[{"id":3,"name":"小组X"}]}
]
  • 调用函数:
getNode(tree, 3)
  • 返回结果:
  • 结果将是:
javascript

{"id":3,"name":"小组X"}
注意事项:
  • 函数假设输入的树形结构是有效的,即每个节点都有唯一的id
  • 如果树形结构中存在多个相同的id,函数只会返回第一个找到的匹配项。
返回值:
  • 返回一个对象,表示找到的节点;如果没有找到,则返回null

单条数据权限验证

判断选的数据是否仅有一条,且有权限

export function isOneData(selected, dataRight = null, msg) {
    if (selected.length > 1) {
        AnMsgbox.msgbox({ message: "一次仅能操作一条数据" });
        return false;
    }
    if (selected.length == 0) {
        AnMsgbox.msgbox({ message: "请选择需要操作的数据" });
        return false;
    }
    if (dataRight) {
        if (!selected[0].dataRight[dataRight] ||
            (selected[0].dataRight[dataRight] != "1" && selected[0].dataRight[dataRight] != "2")
        ) {
            AnMsgbox.msgbox({
                message: msg ?? "当前选择的数据无权限",
            });
            return false;
        }
    }

    return true;
}

功能概述

接收参数:
  • selected: 已选中的数据项数组。
  • dataRight: 可选参数,表示需要检查的数据权限键名。
  • msg: 可选参数,自定义消息文本。
功能描述:
  1. 检查数量: 确保selected数组中只有一条数据。
  2. 提示信息: 如果selected数组为空或超过一条数据,则显示提示信息并返回false
  3. 权限检查: 如果指定了dataRight,则检查第一条数据是否具有相应的权限。
  4. 返回结果: 如果通过所有检查,则返回true;否则返回false
示例:
  • 已选中数据:
javascript[
  {"id":1,"name":"张三","dataRight":{"edit":"1"}},
  {"id":2,"name":"李四","dataRight":{"edit":"0"}}
]
  • 调用函数:
isOneData([selected[0]], "edit")
  • 返回结果:
  • 结果将是:
javascript

true
注意事项:
  • 如果selected数组为空或超过一条数据,函数会显示提示信息。
  • 如果指定了dataRight且第一条数据不具有相应的权限,函数也会显示提示信息。
返回值:
  • 返回一个布尔值,表示是否满足条件。

树转列表

将tree转化为list,数据扁平化

export function treeToList(tree) {
    let res = []; // 用于存储递归结果(扁平数据)
    // 递归函数
    let fn = (source) => {
        source.forEach((el) => {
            res.push(el);
            el.children && el.children > 0 ? fn(el.childrenList) : ""; // 子级递归
        });
    };
    fn(tree);
    return res;
}

功能概述

接收参数:
  • tree: 需要转换的树形结构数组。
功能描述:
  1. 初始化结果数组: 创建一个空数组res用于存储扁平化的数据。
  2. 递归函数: 定义一个递归函数fn,用于遍历树形结构并将每个节点添加到结果数组中。
  3. 遍历树: 使用fn函数遍历整个树形结构。
  4. 返回结果: 返回扁平化后的数组res
示例:
  • 树形结构:
javascript[
  {"id":1,"name":"部门A","childrenList":[{"id":2,"name":"部门B"},{"id":4,"name":"小组Y"}]},
  {"id":2,"name":"部门B","childrenList":[{"id":3,"name":"小组X"}]}
]
  • 调用函数:
treeToList(tree)
  • 返回结果:
  • 结果将是:
javascript[
  {"id":1,"name":"部门A"},
  {"id":2,"name":"部门B"},
  {"id":4,"name":"小组Y"},
  {"id":3,"name":"小组X"}
]
注意事项:
  • 函数假设输入的树形结构是有效的,即每个节点都有唯一的id
  • 如果树形结构中存在多个相同的id,函数不会处理这种情况。
  • 函数假设每个节点的子节点列表键名为childrenList
返回值:
  • 返回一个扁平化的数组,包含了原树形结构中的所有节点。

深拷贝对象|数组

深拷贝 传入数组或对象

export function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }

    if (Array.isArray(obj)) {
        const arrCopy = [];
        for (let i = 0; i < obj.length; i++) {
            arrCopy[i] = deepClone(obj[i]);
        }
        return arrCopy;
    }

    const objCopy = {};
    for (const key in obj) {
        if (obj.hasOwnProperty(key)) {
            objCopy[key] = deepClone(obj[key]);
        }
    }

    return objCopy;
}

功能概述

接收参数:
  • obj: 需要深拷贝的对象。
功能描述:
  1. 基础类型处理: 如果obj不是对象或已经是null,则直接返回obj
  2. 数组处理: 如果obj是数组,则创建一个新的数组并递归地深拷贝每个元素。
  3. 对象处理: 如果obj是对象,则创建一个新的对象并递归地深拷贝每个属性。
  4. 返回结果: 返回深拷贝后的对象或数组。
示例:
  • 原始对象:
javascript{
  a: 1,
  b: [1, 2, 3],
  c: { x: 10, y: 20 },
  d: null,
  e: undefined,
  f: function() { console.log("Hello"); }
}
  • 调用函数:
deepClone(originalObj)
  • 返回结果:
  • 结果将是:
javascript{
  a: 1,
  b: [1, 2, 3],
  c: { x: 10, y: 20 },
  d: null,
  e: undefined,
  f: [Function: f]
}
注意事项:
  • 函数支持基本类型、数组和对象的深拷贝。
  • 函数不支持循环引用的对象。
  • 函数不深拷贝函数类型的属性。
返回值:
  • 返回一个深拷贝后的对象或数组。

总结

  1. 数组添加键值对 (addKeysToData):
  • 为数组中的每个对象添加新的键值对。
  • 使用 Vue.set 方法确保 Vue 的响应式更新。
  1. 数组转换为树结构 (arrayToTree):
  • 将一维数组转换为树形结构。
  • 支持对树形结构进行排序。
  1. 合并两数组转树形结构 (twoArrayToTree):
  • 将两个数组合并,并转换为树形结构。
  • 处理 ID 重复问题,适用于两个不同的数据源。
  1. 数组列值组合字符串 (getArrayColStrs):
  • 获取数组中特定键的所有值,并用指定分隔符连接成字符串。
  1. 树形结构查找节点 (getNode):
  • 在树形结构中根据 ID 查找指定节点。
  1. 单条数据权限验证 (isOneData):
  • 验证用户选择的数据是否仅有一条,并且用户对该数据有操作权限。
  1. 树转列表 (treeToList):
  • 将树形结构转换为扁平化的列表。
  1. 深拷贝对象或数组 (deepClone):
  • 实现对象或数组的深拷贝功能。