js树形结构递归_js树形结构遍历所有子节点

js树形结构递归_js树形结构遍历所有子节点1、获取树中的所有祖先节点名称2、根据过滤条件筛选出需要留下节点的树结构数据3、获取树中叶子节点的总个数4、重新组合树结构中的数据5、根据id和pid把json结构转树状结构_js递归遍历树结构

一、获取树中的所有祖先节点名称

一般用于手动设置vue/react的UI库中的树默认全部展开节点。

const treeData = [{ 
   
    key: '全部',
    title: '全部',
    isLeaf: false,
    children: [{ 
   
        key: '数据库',
        title: '数据库',
        isLeaf: false,
        children: [
            { 
   
                key: 'mysql',
                title: 'mysql',
                isLeaf: false,
                children: [
                    { 
   
                        key: '137',
                        title: 'QPS',
                        isLeaf: true,
                    }, { 
   
                        key: '146',
                        title: 'MySQL进程信息',
                        isLeaf: true,
                    },
                ],
            },
            { 
   
                key: 'oracle',
                title: 'oracle',
                isLeaf: false,
                children: [
                    { 
   
                        key: '137',
                        title: 'QPS',
                        isLeaf: true,
                    },
                ],
            },
        ],
    }],
}];

/** * 获取树的所有祖先节点指定的key名 * @param {Array} treeData 树数据 * @param {String} childrenStr 子节点的键名 * @param {String} keyStr 获取指定的key名 * @returns 获取树链路上所有指定的key名数组 */
export const getTreeParents = (treeData, childrenStr = 'children', keyStr = 'key') => { 
   
  const parentKey = []
  treeData.forEach((item) => { 
   
    if (item[childrenStr] && item[childrenStr].length) { 
   
      parentKey.push(item[keyStr])
      const temp = getTreeParents(item[childrenStr], childrenStr, keyStr)
      if (temp.length) { 
   
        parentKey.push(...temp)
      }
    }
  })
  return parentKey
}

const parentKeys = getTreeParents(treeData);
console.log(parentKeys); // ["全部", "数据库", "mysql", "oracle"]

二、获取树中指定节点的祖先节点id集合

一般用于手动设置vue/react的UI库中的树默认展开指定菜单项。

const tree = [
  { 
   
    id: 11524,
    text: '店铺',
    leaf: false,
    children: [
      { 
   
        id: 11525,
        text: '子店铺',
        leaf: false,
        children: []
      }
    ]
  },
  { 
   
    id: 11526,
    text: '品种',
    leaf: false,
    children: [
      { 
   
        id: 11527,
        text: '类目',
        leaf: false,
        children: [
          { 
   
            id: 11530,
            text: '一级分类',
            leaf: false,
            children: []
          },
          { 
   
            id: 11531,
            text: '二级分类',
            leaf: false,
            children: []
          },
          { 
   
            id: 11532,
            text: '三级分类',
            leaf: false,
            children: []
          }
        ]
      },
      { 
   
        id: 11528,
        text: '产品',
        leaf: false,
        children: [
          { 
   
            id: 11533,
            text: '单品',
            leaf: false,
            children: []
          },
          { 
   
            id: 11534,
            text: '套装',
            leaf: false,
            children: []
          }
        ]
      },
      { 
   
        id: 11529,
        text: '价格',
        leaf: false,
        children: [
          { 
   
            id: 11535,
            text: '价格策略',
            leaf: false,
            children: []
          }
        ]
      }
    ]
  },
  { 
   
    id: 11536,
    text: '店铺计划',
    leaf: false,
    children: [
      { 
   
        id: 11537,
        text: '零售计划',
        leaf: false,
        children: []
      },
      { 
   
        id: 11538,
        text: '品种计划',
        leaf: false,
        children: []
      }
    ]
  },
  { 
   
    id: 11539,
    text: '基础配置',
    leaf: false,
    children: [
      { 
   
        id: 11540,
        text: '平台配置',
        leaf: false,
        children: []
      }
    ]
  }
]
/** * 获取树指定节点的祖先id集合 * @param {Array} treeData 树数据 * @param {String/Number} id 要指定的子节点的id * @returns 获取树指定节点的祖先们id数组 */
const getParentIdList = (treeData, id) => { 
   
  if (!treeData || !id) { 
   
    return ''
  }
  const arr = []
  const findParent = (data, nodeId, parentId = '') => { 
   
    for (var i = 0, length = data.length; i < length; i++) { 
   
      let node = data[i]
      if (node.id === nodeId) { 
   
        node.children?.length && arr.unshift(nodeId)
        // 如果已经到达最顶层则退出循环
        if (nodeId === treeData[0].id) { 
   
          break
        }
        findParent(treeData, parentId)
        break
      } else { 
   
        if (node.children?.length) { 
   
          findParent(node.children, nodeId, node.id)
        }
        continue
      }
    }
    return arr
  }
  return findParent(treeData, id)
}

const parentIds = getParentIdList(tree, 11534)
console.log('parentIds:', parentIds) // [11526, 11528]

三、根据过滤条件筛选出需要留下节点的树结构数据

一般用于前端做树的查询功能。

/** * 递归过滤节点,但保留原树结构,即符合条件节点的父路径上所有节点不管是否符合条件都保留 * @param {Node[]} nodes 要过滤的树 * @param {node => boolean} predicate 过滤条件,符合条件的节点保留(参数为函数,返回值为布尔值) * @param {node => boolean} wrapMatchFn 层级条件(参数为函数,返回值为布尔值) * @return 过滤后的包含根节点数组 */
export const filterSearchTree = (nodes, predicate, wrapMatchFn = () => false) => { 
   
  // 如果已经没有节点了,结束递归
  if (!(nodes && nodes.length)) { 
   
    return []
  }
  const newChildren = []
  for (let i = 0; i < nodes.length; i++) { 
   
    const node = nodes[i]
    // 想要截止匹配的那一层(如果有匹配的则不用递归了,直接取下面所有的子节点)
    if (wrapMatchFn(node) && predicate(node)) { 
   
      newChildren.push(node)
      continue
    }
    const subs = filterSearchTree(node.children, predicate, wrapMatchFn)

    // 以下两个条件任何一个成立,当前节点都应该加入到新子节点集中
    // 1. 子孙节点中存在符合条件的,即 subs 数组中有值
    // 2. 自己本身符合条件
    if ((subs && subs.length) || predicate(node)) { 
   
      node.children = subs || []
      newChildren.push(node)
    }
  }
  return newChildren.length ? newChildren : []
}

const treeData = [{ 
   
    key: '全部',
    title: '全部',
    isLeaf: false,
    children: [{ 
   
        key: '数据库',
        title: '数据库',
        isLeaf: false,
        children: [
            { 
   
                key: 'mysql',
                title: 'mysql',
                isLeaf: false,
                children: [
                    { 
   
                        key: '142',
                        title: '慢查询',
                        isLeaf: true,
                    }, { 
   
                        key: '137',
                        title: 'QPS',
                        isLeaf: true,
                    }, { 
   
                        key: '143',
                        title: '用户列表',
                        isLeaf: true,
                    },
                ],
            },
            { 
   
                key: '166',
                title: 'SQL',
                isLeaf: true,
            },
        ],
    }],
}];

// 要查找的关键字
const searchValue = 'S';

// 筛选到的树结构数据
const newTreeData = filterSearchTree(
    treeData,
    (node) => { 
   
        if (node.title.indexOf(searchValue) !== -1) { 
   
            return true;
        }
        return false;
    },
);
// const newTreeData = filterSearchTree(
// cloneTreeDatas,
// (node) => { 
   
// if (node.title.includes(searchValue)) { 
   
// return true
// }
// return false
// },
// (node) => { 
   
// // 第二层(显示左侧角色组数据)
// if (node.groupName) { 
   
// return true
// }
// return false
// }
// )
console.log(JSON.stringify(newTreeData));
// 打印的结果
// [{ 
   
// key: '全部',
// title: '全部',
// isLeaf: false,
// children: [{ 
   
// key: '数据库',
// title: '数据库',
// isLeaf: false,
// children: [{ 
   
// key: 'mysql',
// title: 'mysql',
// isLeaf: false,
// children: [{ 
   
// key: '137',
// title: 'QPS',
// isLeaf: true,
// children: null,
// }],
// }, { 
   
// key: '166',
// title: 'SQL',
// isLeaf: true,
// children: null,
// }],
// }],
// }];

四、获取树中叶子节点的总个数(节点中isLeaf为true的节点)

const treeData = { 
   
    name: '2021资源',
    title: '2021资源',
    key: '1',
    isLeaf: false,
    children: [{ 
   
        name: '服务器',
        isLeaf: false,
        title: '服务器',
        key: '6',
        children: [
            { 
   
                name: '172.168.201.109',
                isLeaf: false,
                title: '172.168.201.109',
                key: '5',
                children: [
                    { 
   
                        name: 'ping丢包率',
                        isLeaf: true,
                        children: null,
                        title: 'ping丢包率',
                        key: '2',
                    }, { 
   
                        name: 'ping状态',
                        isLeaf: true,
                        children: null,
                        title: 'ping状态',
                        key: '3',
                    },
                ],
            },
            { 
   
                name: '192.168.3.6',
                isLeaf: true,
                children: null,
                title: '192.168.3.6',
                key: '7',
            },
        ],
    }],
};

const getLeafNum = (treeNode) => { 
   
    let leafNum = 0;
    if (!treeNode) { 
   
        return leafNum;
    }
    if (treeNode.children && treeNode.children.length) { 
   
        treeNode.children.forEach((item) => { 
   
            leafNum += getLeafNum(item);
        });
    } else { 
   
        if (treeNode.isLeaf) { 
   
            leafNum++;
        }
    }
    return leafNum;
};
console.log(getLeafNum(treeData)); // 3

五、重新组合树结构中的数据

一般用于因后端给的数据字段不够完善,类型不正确,需要前端进行处理树数据。

const treeData = [{ 
   
    name: '2021资源',
    key: '1',
    isLeaf: false,
    children: [{ 
   
        name: '服务器',
        isLeaf: false,
        key: '6',
        children: [
            { 
   
                isLeaf: false,
                name: '172.168.201.109',
                key: '5',
                children: [
                    { 
   
                        isLeaf: true,
                        children: [],
                        name: 'ping丢包率',
                        key: '2',
                    }, { 
   
                        isLeaf: true,
                        children: [],
                        name: 'ping状态',
                        key: '3',
                    },
                ],
            },
            { 
   
                isLeaf: true,
                children: [],
                name: '192.168.3.6',
                key: '7',
            },
        ],
    }],
}];

// 重新组合树数据(根据需要来重组树结构中的属性字段)
const dealTreeData = (treeData) => { 
   
    const data = treeData.map((item) => ({ 
   
        ...item,
        // 新增title字段
        title: item.name,
        // 如果children为空数组,则置为null
        children: (item.children && item.children.length)
            ? dealTreeData(item.children)
            : null,
    }));
    return data;
};

console.log(JSON.stringify(dealTreeData(treeData)));
// 打印结果
// [{ 
   
// name: '2021资源',
// key: '1',
// isLeaf: false,
// title: '2021资源',
// children: [{ 
   
// name: '服务器',
// isLeaf: false,
// key: '6',
// title: '服务器',
// children: [{ 
   
// isLeaf: false,
// name: '172.168.201.109',
// key: '5',
// title: '172.168.201.109',
// children: [{ 
   
// isLeaf: true,
// children: null,
// name: 'ping丢包率',
// key: '2',
// title: 'ping丢包率',
// }, { 
   
// isLeaf: true,
// children: null,
// name: 'ping状态',
// key: '3',
// title: 'ping状态',
// }],
// }, { 
   
// isLeaf: true,
// children: null,
// name: '192.168.3.6',
// key: '7',
// title: '192.168.3.6',
// }],
// }],
// }];

六、根据id和pid把json结构 转 树状结构

const jsonData = [
    { 
    id: '0', pid: '-1', name: '666' },
    { 
    id: '4', pid: '1', name: '大家电' },
    { 
    id: '5', pid: '1', name: '生活电器' },
    { 
    id: '1', pid: '0', name: '家用电器' },
    { 
    id: '2', pid: '0', name: '服饰' },
    { 
    id: '3', pid: '0', name: '化妆' },
    { 
    id: '7', pid: '4', name: '空调' },
    { 
    id: '8', pid: '4', name: '冰箱' },
    { 
    id: '9', pid: '4', name: '洗衣机' },
    { 
    id: '10', pid: '4', name: '热水器' },
    { 
    id: '11', pid: '3', name: '面部护理' },
    { 
    id: '12', pid: '3', name: '口腔护理' },
    { 
    id: '13', pid: '2', name: '男装' },
    { 
    id: '14', pid: '2', name: '女装' },
    { 
    id: '15', pid: '7', name: '海尔空调' },
    { 
    id: '16', pid: '7', name: '美的空调' },
    { 
    id: '19', pid: '5', name: '加湿器' },
    { 
    id: '20', pid: '5', name: '电熨斗' },
    { 
    id: '21', pid: '20', name: '电熨斗子项' },
];

/** * 根据id和pid把json结构 转 树状结构 * @param jsonArr {json} json数据 * @param idStr {String} id的属性名 * @param pidStr {String} 父id的属性名 * @param childrenStr {String} children的属性名 * @return {Array} 数组 */
const transData = (jsonArr, idStr, pidStr, childrenStr) => { 
   
    // 存放的最终结果树数组
    const result = [];
    const id = idStr;
    const pid = pidStr;
    const children = childrenStr;
    const len = jsonArr.length;

    // 遍历得到以id为键名的对象(建立整棵树的索引)
    const hash = { 
   };
    jsonArr.forEach(item => { 
   
    		 hash[item[id]] = item;
    });

    for (let j = 0; j < len; j++) { 
   
        const jsonArrItem = jsonArr[j];
        const hashItem = hash[jsonArrItem[pid]];
        if (hashItem) { 
   
            // 如果当前项还没有children属性,则添加该属性并设置为空数组
            !hashItem[children] && (hashItem[children] = []);
            hashItem[children].push(jsonArrItem);
        } else { 
   
            result.push(jsonArrItem);
        }
    }
    return result;
};

const jsonDataTree = transData(jsonData, 'id', 'pid', 'children');
console.log(jsonDataTree);

输出结果:
在这里插入图片描述

七、打平树结构数据

// 把树结构数据打平为一维数组
const flatTree = (treeData) => { 
   
  let result = []
  treeData.forEach((item) => { 
   
    // 先克隆一份数据作为第一层级的填充
    let res = JSON.parse(JSON.stringify(item))
    delete res.children
    result.push(res)
    if (item.children && item.children.length > 0) { 
   
      // 如果当前children为数组并且长度大于0,才可进入flatTree()方法
      result = result.concat(flatTree(item.children))
    }
  })
  return result
}

今天的文章js树形结构递归_js树形结构遍历所有子节点分享到此就结束了,感谢您的阅读。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:http://bianchenghao.cn/67877.html

(0)
编程小号编程小号

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注