递归实现数组扁平化-我钻进去了

232 阅读3分钟

在日常开发中,对于一些嵌套(套娃)比较多的数据,我们首先会想到的是用嵌套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的,有些是属于子部门,还有些是属于子部门的子部门。

employee01.png

上实现代码👇

//函数最终  返回  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
}

静静地理解一下其中的递归:

employee02.png

用代码标注图标注一下每一次取出不同层级数据的过程:

employee04.png

其中,每一次递归调用自身,都会重新执行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%")
  }
}

输出效果:

employee05.png

对比数据源treeData和屏幕结果不难发现,在递归扁平化数组的过程中,递归函数是广度优先搜索的。

感谢阅读!如有欠缺之处欢迎指正,三克油!