js 常见递归方式
·
js 平铺
js 数组平铺
- 递归法
var arr = [1,2,[3,4,5,[6,7,8],9],10,[11,12]];
function fn(arr){
let arr1 = []
arr.forEach((val)=>{
if(val instanceof Array){
console.log(1)
arr1 = arr1.concat(fn(val))
}else{
arr1.push(val)
}
})
return arr1
}
fn(arr)
- reduce
function fn(arr) {
return arr.reduce((prev, cur) => {
return prev.concat(Array.isArray(cur) ? fn(cur) : cur)
}, [])
}
- ES6 flat(Infinity)
let arr2 = arr.flat(Infinity)
js 数组对象平铺
const json = [
{
id: 1,
title: "课程1",
children: [
{ id: 4, title: "课程1-1" },
{
id: 5,
title: "课程1-2",
children: [
{ id: 6, title: "课程1-2-1" },
{ id: 7, title: "课程1-2-2" },
],
},
],
},
{ id: 2, title: "课程2" },
{ id: 3, title: "课程3" },
];
let arr = [];
function fn(j) {
j.forEach(item => {
arr.push(item)
//循环判断是否有children
if (item.children) {
//如果有 递归去除
fn(item.children)
}
})
// console.log(1)
console.log('arr',JSON.stringify(arr))
// debugger
arr.forEach(item => {
// 删除children
delete item.children
})
//
}
fn(json)
js 数组对象平铺
const flatData = (data) => {
return data.reduce((prev, curr) => {
prev.push(curr);
if (curr.children &&curr.children.length > 0) {
prev.push(...flatData(curr.children));
// 删除children
delete curr.children
}
return prev;
}, []);
};
}
// 把树结构数据打平为一维数组
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 递归
简单示例
//必须要有if判断进行出栈,不然会进行死循环
<script>
function factorial(n) {
if (n == 1) return n;
return n * factorial(n - 1)
}
console.log(factorial(5)) // 5 * 4 * 3 * 2 * 1 = 120
</script>
js树结构数据的递归
export const getTreeData = (nodes, nodeId,propertiesInp)=>{
for(var i=0;i<nodes.length;i++){
let node = nodes[i]
let {children} = node
if(node.nodeId === nodeId){
node.input = {
...node.input,
...{
properties: {...propertiesInp}
}
}
}
if(children && children.length> 0){
getTreeData(children, nodeId, propertiesInp)
}
}
}
js找上级节点
数据结构
let data = [
{
"id": 1,
"label": "猫爷爷",
"children": [
{
"id": 2,
"label": "猫爸1",
"children": [
{
"id": 3,
"label": "猫崽1-1",
},
{
"id": 4,
"label": "猫崽1-2",
}
],
},
{
"id": 5,
"label": "猫爸2",
"children": [
{
"id": 6, //根据这个id,查找它的所有上级
"label": "猫崽2-1",
},
{
"id": 7,
"label": "猫崽2-2",
},
],
},
]
},
{
"id": 7,
"label": "猪爷爷",
"children": [
{
"id": 8,
"label": "猪爸1",
"children": [
{
"id": 9,
"label": "猪崽1",
},
{
"id": 10,
"label": "猪崽2",
}
],
},
]
},
]
JS树形结构,通过子节点,查找上级所有父节点
let target = { "id": 6, "label": "猫崽2-1" }
let result = []
this.findParent(data, target, result)
console.log(result) // 打印出结果:['猫爷爷', '猫爸2', '猫崽2-1']
//data:要遍历的数据, target:查找目标, result用于装查找结果的数组
findParent(data, target, result) {
for (let i in data) {
let item = data[i]
if (item.id === target.id) {
//将查找到的目标数据加入结果数组中
//可根据需求unshift(item.id)或unshift(item)
result.unshift(item.label)
return true
}
if (item.children && item.children.length > 0) {
let ok = this.findParent(item.children, target, result)
if (ok) {
result.unshift(item.label)
return true
}
}
}
//走到这说明没找到目标
return false
}
获取树中的所有祖先节点名称
/**
* 获取树的所有祖先节点指定的key名
* @param {Array} treeData 树数据
* @param {String} childrenStr 子节点的键名
* @param {String} keyStr 获取指定的key名
* @returns 获取树链路上所有指定的key名数组
*/
const getTreeParents= (treeData, childrenStr = 'children', keyStr = 'label') => {
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(data);
扩展
据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 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',
// }],
// }],
// }];
获取树中叶子节点的总个数(节点中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
根据过滤条件筛选出需要留下节点的树结构数据: 一般用于前端做树的查询功能。
/**
* 递归过滤节点,但保留原树结构,即符合条件节点的父路径上所有节点不管是否符合条件都保留
* @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,
// }],
// }],
// }];
参考链接 : https://blog.csdn.net/helloxiaoliang/article/details/128646399
更多推荐
已为社区贡献1条内容
所有评论(0)