在日常开发中,对于一些嵌套(套娃)比较多的数据,我们首先会想到的是用嵌套for循环一层一层的去访问提取想要的数据,但是此法比较吃性能且代码不够优雅。
于是,递归思想就运营而出了。
本文主要讲解一下用递归实现数据扁平化的一些拙见。
上栗子👇
需求:提取出以下数据中的employees员工信息,返回一个Employee类型的数组。
interface Department {
departmentName: string
employees?: Employee[]
children?: Department[]
}
interface Employee {
username: string
avatar: ResourceStr
gender: string
birth: number
}
const treeData: Department[] = [
{
departmentName: '软件开发部',
// 子部门
children: [
{
departmentName: '前端',
children: [
{
departmentName: 'IOS',
employees: [
{
username: '张姗',
avatar: $r('app.media.user_02'),
gender: '女',
birth: 1693039237877,
},
]
},
{
departmentName: 'web',
employees: [
{
username: '张杉',
avatar: $r('app.media.user_03'),
gender: '女',
birth: 1693039237877,
},
{
username: '张彡',
avatar: $r('app.media.user_04'),
gender: '女',
birth: 1693039237877,
},
]
}
],
employees: [
{
username: '张珊',
avatar: $r("app.media.user_01"),
gender: '女',
birth: 1693039237877,
},
]
}],
},
{
departmentName: '人事部',
// 当前部门人员
employees: [
{
username: '李思',
avatar: $r('app.media.user_05'),
gender: '女',
birth: 1693039237877,
},
{
username: '王舞',
avatar: $r('app.media.user_06'),
gender: '女',
birth: 1693039237877,
},
],
},
]
观察Department部门接口并结合treeData,可以看到employees雇员,children子部门是可选类型的,我们可以知道,有些员工是直属Department的,有些是属于子部门,还有些是属于子部门的子部门。
上实现代码👇
//函数最终 返回 Employee[] Employee接口类型的数组
const getEmployeeInfo = (Department: Department[]) => {
const employeeList: Employee[] = []
for (let index = 0; index < Department.length; index++) {
const department = Department[index]
if (department.employees) {
employeeList.push(...department.employees)
}
if (department.children) {
const resultList = getEmployeeInfo(department.children)
employeeList.push(...resultList)
}
}
return employeeList
}
静静地理解一下其中的递归:
用代码标注图标注一下每一次取出不同层级数据的过程:
其中,每一次递归调用自身,都会重新执行for,关键在于每次的department的变化。
① department = 直属员工的部门
② department = 二级员工的部门
③ department = 三级员工的部门
(“部门”指员工当前所在的最小部门单位)
递归函数注意点:
1.先写正常的业务代码,for(),if...;
2.要有终止条件。(此例源数据长度有限,遍历完长度即终止)
最后把提取出的员工数据输出(可视化)
完整代码:
interface Department {
departmentName: string
employees?: Employee[]
children?: Department[]
}
interface Employee {
username: string
avatar: ResourceStr
gender: string
birth: number
}
const treeData: Department[] = [
{
departmentName: '软件开发部',
// 子部门
children: [
{
departmentName: '前端',
children: [
{
departmentName: 'IOS',
employees: [
{
username: '张姗',
avatar: $r('app.media.user_02'),
gender: '女',
birth: 1693039237877,
},
]
},
{
departmentName: 'web',
employees: [
{
username: '张杉',
avatar: $r('app.media.user_03'),
gender: '女',
birth: 1693039237877,
},
{
username: '张彡',
avatar: $r('app.media.user_04'),
gender: '女',
birth: 1693039237877,
},
]
}
],
employees: [
{
username: '张珊',
avatar: $r("app.media.user_01"),
gender: '女',
birth: 1693039237877,
},
]
}],
},
{
departmentName: '人事部',
// 当前部门人员
employees: [
{
username: '李思',
avatar: $r('app.media.user_05'),
gender: '女',
birth: 1693039237877,
},
{
username: '王舞',
avatar: $r('app.media.user_06'),
gender: '女',
birth: 1693039237877,
},
],
},
]
//函数最终 返回 Employee[] Employee接口类型的数组
const getEmployeeInfo = (Department: Department[]) => {
const employeeList: Employee[] = []
for (let index = 0; index < Department.length; index++) {
const department = Department[index]
if (department.employees) {
employeeList.push(...department.employees)
}
if (department.children) {
const resultList = getEmployeeInfo(department.children)
employeeList.push(...resultList)
}
}
return employeeList
}
const list = getEmployeeInfo(treeData)
@Entry
@Component
struct Index {
build() {
List() {
ForEach(list, (item: Employee) => {
ListItem() {
Row({ space: 10 }) {
Image(item.avatar)
.width(60)
Text(item.username)
Text(item.birth.toString())
Text(item.gender)
}
}
.margin({ top: 5 })
})
}.width("100%")
}
}
输出效果:
对比数据源treeData和屏幕结果不难发现,在递归扁平化数组的过程中,递归函数是广度优先搜索的。
感谢阅读!如有欠缺之处欢迎指正,三克油!