树形数据处理方法!!!

209 阅读2分钟

树形数据-业务开发方法

经常在开发中使用的一些方法!!!

列表转换树结构

/**
 * 列表转换树结构
 * @param {Object[]} list 列表数据,约定字段名称: id/parentId/children
 * @param {*} [parentId=null] 父节点的值
 * @param {string} [idKey=id] id字段名称
 * @param {string} [parentIdKey=parentId] parentId字段名称
 * @param {boolean} [withRoot=false] 有根节点id时是否保留返回根节点
 * @return {Array}
 */
export function create(list = [], parentId = null, idKey = 'id', parentIdKey = 'parentId', withRoot = false) {
    const _list = cloneDeep(list);
    const temp = new Map(),
        tree = [];
    _list.forEach((item) => {
        temp.set(item[idKey], { ...item });
    });
    for (const item of temp.values()) {
        if (item[parentIdKey] === parentId) {
            tree.push(item);
        } else {
            const parent = temp.get(item[parentIdKey]);
            if (parent) {
                if (!parent.children) {
                    parent.children = [];
                }
                parent.children.push(item);
            }
        }
    }
    if (parentId && withRoot) {
        const target = list.find((item) => {
            return item[idKey] === parentId;
        });
        if (target) {
            target.children = tree;
            return [target];
        } else {
            return tree;
        }
    } else {
        return tree;
    }
}

遍历树数据节点,查找符合条件的节点

/**
 * 遍历树数据节点,查找符合条件的节点
 * @param {Array|Object} data 数据树,如 {id:1, children:[{id:2}]}
 * @param {Boolean} isFindOne 是否只找最先符合条件的一个
 * @param {Function} fn 查找回调函数,回调参数:item 节点,index节点当前兄弟节点中的索引,data 查找的数据树,函数返回true表示符合条件
 * @param {string} [field=children] 子级字段名称
 * @returns {Array|Object} 查找结果,isFindOne为true时返回Object, false时返回Array
 */
export function find(data = [], isFindOne, fn, field = 'children') {
    let result = [];
    data = Array.isArray(data) ? data : [data];
    for (let i = 0, len = data.length; i < len; i++) {
        const item = data[i],
            checked = fn(item, i, data),
            children = item[field];
        if (checked) {
            result.push(item);
            if (isFindOne) break;
        }
        if (children) {
            const child = find(children, isFindOne, fn, field);
            if (child) result = result.concat(child);
        }
    }
    return isFindOne ? result[0] || null : result;
}

查找节点在树结构数组的路径

/**
 * 查找节点在树结构数组的路径
 * @param {Array|Object} data 树数据数组, 如 {id:1, children:[{id:2}]}
 * @param {Function} fn 查找回调函数,回调参数:item 节点,index节点当前兄弟节点中的索引,data 查找的数据树,函数返回true表示符合条件
 * @param {string} [field=children] 子级字段名称
 * @return {Array} 节点路径数组
 */
export function findPath(data, fn, field = 'children') {
    const path = [];

    function find(array, parent) {
        parent && path.push(parent);
        for (let i = 0, len = array.length; i < len; i++) {
            const item = array[i],
                checked = fn(item, i, array),
                children = item[field];
            // 找到,记录路径,退出循环
            if (checked) {
                path.push(item);
                return true;
            }
            if (children && children.length > 0) {
                // 在子级找到,退出循环,自己没有,删除记录的父级
                if (find(children, item)) {
                    return true;
                } else {
                    path.pop();
                }
            }
        }
    }

    find([].concat(data));
    return path;
}

将树状数据摊平为一维数组

const _flat = function (tree, map = {}, idProp = 'id', childrenProp = 'children') {
    tree.forEach((item) => {
        map[item[idProp]] = item;
        if (item[childrenProp]) {
            _flat(item.children, map);
        }
    });
};
/**
 * 将树状数据摊平为一维数组
 * @param {Array} tree 树数据数组, 如 [{id:1, children:[{id:2}]}]
 * @param {string} [idProp=id] 节点唯一编号字段名称
 * @param {string} [childrenProp=children] 子级字段名称
 * @return {Array} 节点路径数组
 */
 
export function treeRevert(tree, idProp = 'id', childrenProp = 'children') {
    const map = {};
    _flat(tree, map, idProp, childrenProp);
    return Object.values(map);
}

树形结构数据去除空的children

/**
 * 树形结构数据去除空的children
 * @param {*} [] tree 树形结构数组
 * @param {*} field 子节点数组标识
 */
export function treeDchildren(tree = [], field = 'children') {
    return tree.map((item) => {
        if (item[field]?.length > 0) {
            treeDchildren(item[field], field);
        } else {
            delete item[field];
        }
        return item;
    });
}