JS 树结构数据各种递归

递归-----树组件节点属性修改
参数为数组

/**
 * 递归-----树组件节点属性修改
 * @param {Object} data       树的源数据
 * @returns {Object} data     属性修改后的树的数据
 */
setTreeRecursion(data) {
    data.map((item, index) => {
        item.scopedSlots = { title: 'customNode' };
        if(item.children && item.children.length != 0){
            this.setTreeRecursion(item.children);
        }
    });
    return data;
}
setTreeRecursion('树结构数据')
参数为对象

/**
 * 递归-----树组件节点属性修改
 * @param {Object} data       树的源数据
 * @returns {Object} data     属性修改后的树的数据
 */
setTreeRecursion(data) {
    data.scopedSlots = { title: 'customNode' };
    if(data.children && data.children.length != 0){
        data.children.map((item, index) => {
            this.setTreeRecursion(item);
        });
    }
    return data;
}
setTreeRecursion('树结构数据')
递归-----查询 树组件 共有多少节点
参数为数组

/**
 * 递归-----查询 树组件 共有多少节点
 * @param {Object}      data树的源数据
 * @returns {Number}    num 总节点数
 */
queryNodesTotal(data, num = 0){
    let forFn = nodes => {
        for(let i = 0; i < nodes.length; i++){
            num++;
            if(nodes[i].children && nodes[i].children.length != 0){
                forFn(nodes[i].children);
            }
        }
    }
    forFn(data);
    return num;
}
queryNodesTotal('树结构数据')
参数为对象

/**
 * 递归-----查询 树组件 共有多少节点
 * @param {Object}      data树的源数据
 * @returns {Number}    num 总节点数
 */
queryNodesTotal(data, num = 0){
    let forFn = nodes => {
        num++;
        if(nodes.children && nodes.children.length != 0){
            for(let i = 0; i < nodes.children.length; i++){
                forFn(nodes.children[i]);
            }
        }
    }
    forFn(data);
    return num;
}
queryNodesTotal('树结构数据')
/**
 * 递归-----查询 树组件 共有多少节点
 * @param data  data树的源数据
 * @param num num 总节点数
 * @returns {{checkTotal: number, total: number}} checkTotal: 选中的节点总数,total: 所有节点总数
 */
queryNodesTotal(data, num = { total: 0, checkTotal: 0 }){
    let forFn = nodes => {
        for(let i = 0; i < nodes.length; i++){
            if(nodes[i].check != undefined && nodes[i].check != null){
                num.total++;
                if(nodes[i].check) num.checkTotal++;
            }

            if(nodes[i].children && nodes[i].children.length != 0){
                forFn(nodes[i].children);
            }
        }
    }
    forFn(data);
    return num;
}

queryNodesTotal('树结构数据')
递归-----取出树组件最后子节点
/**
 * 递归-----取出树组件最后子节点---对象
 * @param {Object} node      树的源数据
 * @returns {Object} temp    取出树组件最后子节点的集合
 */
getLastChildNode(node, temp = []) {
    let forFn = (arr) => {
        for (let i = 0; i < arr.length; i++) {
            if (!arr[i].children || arr[i].children.length == 0) {
                temp.push(arr[i]);
            } else {
                forFn(arr[i].children);
                // if(arr[i].children && arr[i].children.length != 0){
                    // forFn(arr[i].children);
                // }
            }
        }
    }
    forFn(node)
    return temp;
}
getLastChildNode('树结构数据')
/**
 * 递归-----取出树组件最后子节点---数组
 * @param {Object} node      树的源数据
 * @returns {Object} temp    取出树组件最后子节点的集合
 */
getLastChildNode(node, temp = []) {
    let forFn = (arr) => {
        for (let i = 0; i < arr.length; i++) {
            if (!arr[i].children || arr[i].children.length == 0) {
                temp.push(arr);
                arr = [];
            } else {
                forFn(arr[i].children);
            }
        }
    }
    forFn(node)
    return temp;
}
getLastChildNode('树结构数据')
递归-----删除树组件源数据的最后子节点
/**
 * 递归-----删除树组件源数据的最后子节点
 * @param {Object} node      树的源数据
 * @returns {Object} node    删除最后子节点之后的树的数据
 */
deleteSourceNode(node) {
    for (let i = node.length - 1; i >= 0; i--) {
        if (!node[i].hasOwnProperty('children')) {
            continue;
        }
        if (!node[i].children || node[i].children.length == 0) {
            node.splice(i, 1);
            continue;
        }
        this.deleteSourceNode(node[i].children);
    }
    return node;
}
deleteSourceNode('树组件数据')
递归-----根据 id 查询当前节点 或者 根据 pId 查询当前节点的父节点 或者 所有父级点集合
/**
 * 递归-----根据 id 查询当前节点 或者 根据 pId 查询当前节点的父节点
 * @param node      树的源数据
 * @param nodeId    节点的 id 或者 pId
 * @returns temp    返回的匹配的节点数据
 */
queryNodeById(node, nodeId, temp = {}){
    let forFn = (arr, id) => {
        for (let i = 0; i < arr.length; i++) {
            if (arr[i].id === id) {
                temp = arr[i]; // 匹配到的节点 或者 父节点
                break;
            } else {
                if (arr[i].children && arr[i].children.length != 0) {
                    forFn(arr[i].children, id);
                }
            }
        }
    }
    forFn(node, nodeId);
    return temp;
}
queryNodeById('树组件数据', '节点的 id 或者 pId')
/**
 * 递归-----根据 根据 pId 查询当前节点的 所有父级点集合
 * @param node      树的源数据
 * @param pId       节点的 pId
 * @returns temp    返回的匹配的节点数据
 */
queryNodeById(node, pId, temp = []){
    let forFn = (arr, id) => {
        for (let i = 0; i < arr.length; i++) {
            if (arr[i].id === id) {
                temp.push(arr[i]); // 匹配到的节点 push 到 temp 中
                forFn(node, arr[i].pId); // 继续递归查询
                break;
            } else {
                if (arr[i].children && arr[i].children.length != 0) {
                    forFn(arr[i].children, id);
                }
            }
        }
    }
    forFn(node, pId);
    return temp;
}
queryNodeById('树组件数据', '节点的 pId')

`此方法的第二个参数也可以传入 节点的 id,如果传入的是 节点的 id, 则返回的是包含此节点以及所有父级节点

如果传入的仅是此节点的 pId,返回的是不包含此节点的所有父级节点`
/**
 * 递归-----根据 id 查询当前节点 和 根据 pId 查询 当前节点的所有父级节点集合
 * @param node      树的源数据
 * @param nodeId    节点的 id
 * @param nodePid   节点的 pId
 * @param temp      返回的匹配的节点数据集合
 * @returns {{pNodes: *[], cNode: {}}} pNodes: 父级节点集合,cNode:当前节点
 */
queryNodeById(node, nodeId, nodePid, temp = {cNode: {}, pNodes: []}){
    let forFn = (arr, id, pId) => {
        for (let i = 0; i < arr.length; i++) {

            if (arr[i].id === id) {
                temp.cNode = arr[i];
                break;
            }else if (arr[i].id === pId) {
                temp.pNodes.push(arr[i]);
                forFn(node, id, arr[i].pId);
                break;
            } else {
                if (arr[i].children && arr[i].children.length != 0) {
                    forFn(arr[i].children, id, pId);
                }
            }

        }
    }
    forFn(node, nodeId, nodePid);
    return temp;
}

queryNodeById('树组件数据', '节点的 id', '节点的 pId')

上述 三种方法,都是获取当前节点以及父级节点的方法,如果想要获取当前节点以及子级节点,可以按照上边的方法自行修改

递归-----树形数据(数组)获取最深层级数
/**
 * 递归-----树形数据(数组)获取最深层级数
 * @param {Object} treeData   树的源数据
 * @returns {Number} max      最深层级数的值
 */
getMaxFloor(treeData) {
    let floor = 0;
    let max = 0;
    let deepEach = (data, floor) => {
        data.map((item, index) => {
            item.floor = floor;
            if (floor > max) {
                max = floor;
            };
            if (item.children && item.children.length != 0) {
                deepEach(item.children, floor + 1);
            };
        });
    };
    deepEach(treeData, 1);
    return max;
}
getMaxFloor('树组件数据')
递归-----将树结构数据格式,转化为 二维数组 表格形式
node 参数为 数组

/**
 * 递归-----将树结构数据格式,转化为 二维数组 表格形式
 * @param node  树的源数据
 * @param data  树转化为二维数组的数据
 * @param row   临时存储数据
 * @returns {*[]}
 */
parseTreeToRow(node, data = [], row = []) {
    node.map(item => {
        let obj = {
            id: item.id,
            pId: item.pId,
            label: item.label
        }
        if(item.children && item.children.length != 0){
            this.parseTreeToRow(item.children, data, [...row, obj]);
        }else{
            data.push([...row, obj])
        }
    })
    return data;
}

this.parseTreeToRow('树的源数据---数组');
node 参数为 对象

/**
 * 递归-----将树结构数据格式,转化为 二维数组 表格形式
 * @param node  树的源数据
 * @param data  树转化为二维数组的数据
 * @param row   临时存储数据
 * @returns {*[]}
 */
parseTreeToRow(node, data = [], row = []) {
    if (node.children && node.children.length != 0) {
        node.children.map((item, index) => {
            const obj = {
                id: item.id,
                pId: item.pId,
                label: item.label,
            };
            this.parseTreeToRow(item, data, [...row, obj]);
        });
    } else {
        data.push(row);
    }
    return data;
}

// 需要一个有且只有一个根节点的数据
let obj = {
    id: '',
    pId: '',
    label: '',
    children: '树组件数据'
}
this.parseTreeToRow(obj);
递归----- tree 转 一维数组
/**
 * 递归----- tree 转 一维数组
 * @param treeData
 * @param arr
 * @returns {*[]}
 */
treeToArray(treeData = [], arr = []) {
    for (let item of treeData) {
        arr.push(item);
        if (item.children && item.children.length != 0) {
            this.treeToArray(item.children, arr);
        }
    }
    return arr;
}

// 第二种方式
const treeToArray(node){
    const nodeToArray = (node, arr) => {
        const { children, ...item } = node;
        arr.push(item);
        children.forEach(child => nodeToArray(child, arr));
        return arr;
    };
    return nodeToArray(node, []);
}
递归----- 一维数组 转 tree
let data = [
    { id: '1', pId: '0', label: "一级菜单-1" }, 
    { id: '1-2', pId: '1', label: "二级菜单-1-2" },
    { id: '1-3', pId: '1-2', label: "三级菜单-1-3" },
    { id: '1-4', pId: '1-3', label: "四级菜单-1-4" },

    { id: '2', pId: '0', label: "一级菜单-2" },
    { id: '2-1', pId: '2', label: "二级菜单-2-1" },
    { id: '2-1-1', pId: '2-1', label: "三级菜单-2-1-1" },
    { id: '2-1-2', pId: '2-1', label: "三级菜单-2-1-2" }
];

/**
 * 递归----- 一维数组 转 tree (此方法会改变原始数组 data,使用时请注意)
 * @param {Object} data 一维数组
 * @param {Object} pId 最外层父 id (一般为 0)
 */
arrayToTree(data, pId) {
    if (!Array.isArray(data) || !data.length) return [];
    let tree = [];
    let temp;
    for (let i = 0; i < data.length; i++) {
        if (data[i].pId == pId) {
            let obj = data[i];
            temp = this.arrayToTree(data, data[i].id);
            if (temp.length > 0) {
                obj.children = temp;
            }
            tree.push(obj);
        }
    }
    return tree;
}
arrayToTree(data, pId) {
    if (!Array.isArray(data) || !data.length) return [];
    let newArr = [];
    data.forEach(item => {
        if (item.pId == pId) {
            newArr.push({
                ...item, children: this.arrayToTree(data, item.id);
            })
        }
    })
    return newArr;
}

// 调用
arrayToTree(data, '0');

// 第二种方式:双重循环
arrayToTree(data, pId){
    if (!Array.isArray(data) || !data.length) return [];
    // 克隆一个新数组
    let copyArr = Object.assign([],data)
    // 双重循环
    let newArr = copyArr.filter(item =>{
        data.forEach(v =>{
            // 判断 id 和 pId 是否相同
            if(item.id === v.pId){
                // 相同就判断有没有 children , 有就 push 进去 v 没有就 [v]
                if(item.children){
                    item.children.push(v)
                }else{
                    item.children = [v]
                }
            }
        })
 
        if (item.pId == pId) return item
    })
    return newArr
}
// 调用
arrayToTree(data, '0');

// 第三种方式:map 特性模式
arrayToTree(data, pId){
    const tree = [];
    const idMap = new Map();
    data.forEach(item => {
        idMap.set(item.id, item);
    });
    data.forEach(item => {
        if (idMap.has(item.pId)) {
            const parent = idMap.get(item.pId);
            if (!parent.children) {
                parent.children = [];
            }
            parent.children.push(item);
        } else {
            
            tree.push(item);
        }
    });
    return tree;
}

arrayToTree(data, pId) {
    if (!Array.isArray(data) || !data.length) return [];
    let tree = [];
    let map = {};
    data.forEach(item => map[item.id] = item);
    data.forEach(item => {
        let mapPid = map[item.pId];
        if (item.pId == pId){
            tree.push(item);
        }else {
            if (mapPid) {
                ((mapPid.childer || (mapPid.childer = [])).push(item));
            }
        }
    })
    return tree;
}

// 调用
arrayToTree(data, '0');

// 第四种模式:过滤器的模式 
arrayToTree(data, pId){
    if (!Array.isArray(data) || !data.length) return [];
    // 克隆一个新数组
    let tree = data.filter(item =>{
        let children = data.filter(v => item.id === v.pId);
        item.children = children.length > 0 ? item.children = children : [];
        if (item.pId == pId){
            return item;
        }
    })
    return tree;
}

// 调用
arrayToTree(data, '0');
递归-----根据特定数组 匹配 tree 数据,仅保留匹配项
let brr = ['1', '2', '3']
let arr = [
    {
        id: '1',
        pid: '0',
        name: '1',
        children: [
            {
                id: '1-1',
                pid: '0',
                name: '1-1',
                children: []
            }
        ]
    },
    {
        id: '2',
        pid: '0',
        name: '2',
        children: []
    },
    {
        id: '3',
        pid: '0',
        name: '3',
        children: []
    }
];

/**
 * 递归-----根据特定数组 匹配 tree 数据,仅保留匹配项
 * @param localRoute 第一个比较的数组
 * @param returnRoute 第二个比较的数组
 * @returns {*}
 */
matchSpecialData(localRoute, returnRoute) {
    for (let i = 0; i < localRoute.length; i++) {
        if(localRoute[i].children && localRoute[i].children.length != 0){
            this.matchSpecialData(localRoute[i].children, returnRoute);
        }
        let flag = false;
        for(let j = 0; j < returnRoute.length; j++){
            if(localRoute[i].name == returnRoute[j]){
                flag = true;
                break;
            }
        }
        if(!flag){
            localRoute.splice(i--, 1);
        }
    }
    return localRoute;
}

this.matchSpecialData(arr, brr);
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,504评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,434评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,089评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,378评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,472评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,506评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,519评论 3 413
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,292评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,738评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,022评论 2 329
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,194评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,873评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,536评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,162评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,413评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,075评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,080评论 2 352

推荐阅读更多精彩内容