数组递归汇总

91 阅读1分钟

type RowData = {
  id: string
  pid: string
  price: number
  count: number
  totalPrice: number
  isLeaf: boolean
}

const list: RowData[] = [
  { id: '1', pid: '', price: 0, count: 3, totalPrice: 0, isLeaf: false },
  { id: '1.1', pid: '1', price: 10, count: 20, totalPrice: 0, isLeaf: true },
  { id: '1.2', pid: '1', price: 20, count: 30, totalPrice: 0, isLeaf: true },

  { id: '2', pid: '', price: 0, count: 4, totalPrice: 0, isLeaf: false },
  { id: '2.1', pid: '2', price: 0, count: 2, totalPrice: 0, isLeaf: false },
  { id: '2.1.1', pid: '2.1', price: 1, count: 10, totalPrice: 0, isLeaf: true },
  { id: '2.1.2', pid: '2.1', price: 12, count: 5, totalPrice: 0, isLeaf: true },
]

function calculateTotalPrice(list: RowData[]): void {
  // update leaf total price
  const arr1 = list.map((v) => {
    if (v.isLeaf) {
      return { ...v, totalPrice: v.price * v.count }
    } else {
      return v
    }
  })

  // set node id path
  function getNodePath(id: string, arr: RowData[]): string[] {
    const node = arr.find((v) => v.id === id)
    if (node?.pid) {
      const path = getNodePath(node.pid, arr)
      path.push(node.id)
      return path
    }
    return [id]
  }

  // 添加 path 属性,用于排序
  const arr2: (RowData & { path: string[] })[] = arr1.map((v) => ({ ...v, path: getNodePath(v.id, arr1) }))

  // 排序后的数组,子节点排前面
  const arr3 = arr2.sort((a, b) => b.path.length - a.path.length)

  // 计算所有父节点的 price
  const arr4: (RowData & { path: string[] })[] = arr3.map(({ ...rest }) => rest)
  arr4.forEach((v, _i, arr) => {
    if (!v.isLeaf) {
      const children = arr.filter((v2) => v2.path.includes(v.id) && v2.id !== v.id)
      v.price = children.reduce((total, v2) => total + v2.totalPrice, 0)
      v.totalPrice = v.price * v.count
    }
  })

  console.log(arr4)
}

calculateTotalPrice(list)
import { useEffect, useState } from 'react'

export function useOnReady<T>(callback: T, isReady: boolean | (() => boolean)) {
  const [done, set] = useState(false)

  useEffect(() => {
    if (done) return

    if ((typeof isReady === 'boolean' && isReady) || (typeof isReady === 'function' && isReady())) {
      if (typeof callback === 'function') {
        callback()
        set(true)
      }
    }
  }, [callback, isReady, done])
}