JavaScript前端面試扁平數據轉tree與tree數據扁平化

一、寫在前面

有時我們拿到的數據的數據結構可能不是理想的,那麼此時就要求前端程序員,具有改造數據的能力。例如拿到扁平的數據, 但我們要應用在 tree 樹形組件或 Cascader 級聯選擇器組件中,這樣的組件要求數據結構是非扁平的的具有層級遞進關系的 tree 結構。

總之就是說,提供數據的接口給到的數據,未必符合要求,而當我們又無法令他人為為我們改變時,需求和要求就來到瞭前端程序員這裡, 所以得具備這樣的數據處理能力。

下面是將舉兩個數據改造的例子:

  • 一是扁平化,具有層級遞進關系的 tree 數據,轉換為扁平結構的的 flat 數據
  • 二是反扁平化,扁平結構的 flat 數據,轉換為具有層級遞進關系的 tree 數據

二、正文部分

2.1 扁平數據轉為 tree 數據

扁平化函數

  /**
   * 扁平化:將具有層級遞進關系結構的 tree 數據扁平化
   * 
   * @param treeList 有層級遞進關系結構的 tree 數據
   * @param flatList 用於接收扁平化結果的變量
   * @returns {*} 返回扁平化結果
   */
  function treeToFlat (treeList, flatList) {
    // flatList.length > 9999 是考慮底線保護原則,出於極限保護的目的設置的,可不設或按需設置。
    if (flatList.length > 9999) {
      return
    }
    treeList.map(e => {
      flatList.push(e)
      // 遞歸:有條件的自己調用自己,條件是 e.children.length 為真
      if (e.children && e.children.length) {
        treeToFlat(e.children, flatList)
      }
    })
    // console.log('扁平化後:', flatList)
    return flatList
  }

2.2 tree 數據轉為扁平數據

反扁平化函數

  /**
   * 反扁平化:將扁平結構的 flat 數據轉換為具有層級遞進關系結構的 tree 數據
   * 
   * @param flatList 扁平結構的數據
   * @param treeList 用於接收反扁平化結果的變量
   * @returns {*} 返回反扁平化結果
   */
  function flatToTree (flatList, treeList) {
    flatList.map(e => {
      // 以 e.pid===null,作為判斷是不是根節點的依據,或者直接寫死根節點(如果確定的話),
      // 具體以什麼作為判斷根節點的依據,得看數據的設計規則,通常是判斷層級或是否代表根節點的標記
      if (e.pid === null) {
        // 避免出現重復數據
        const index = treeList.findIndex(sub => sub.id === e.id)
        if (index === -1) {
          treeList.push(e)
        }
      }
      flatList.map(e2 => {
        if (e2.pid === e.id) {
          // 避免出現重復數據
          const index = e.children.findIndex(sub => sub.id === e2.id)
          if (index === -1) {
            e.children.push(e2)
          }
        }
      })
    })

2.3 完整測試 demo

demo 測試結果截圖如下:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>扁平數據轉tree與tree數據扁平化 Demo</title>
</head>
<body>
<h1>扁平數據轉tree與tree數據扁平化</h1>
<script>
  window.onload = function () {
    test()
  }
  function test () {
    let flatList = [],
      treeList = [
        {
          id: 1,
          pid: null,
          label: '第一層',
          value: '1',
          children: [
            {
              id: 2,
              pid: 1,
              label: '第二層1',
              value: '2.1',
              children: []
            },
            {
              id: 3,
              pid: 1,
              label: '第二層2',
              value: '2.2',
              children: []
            },
            {
              id: 4,
              pid: 1,
              label: '第二層3',
              value: '2.3',
              children: [
                {
                  id: 5,
                  pid: 4,
                  label: '第三層1',
                  value: '3.1',
                  children: []
                },
                {
                  id: 6,
                  pid: 4,
                  label: '第三層2',
                  value: '3.2',
                  children: []
                },
              ]
            },
          ]
        }
      ]
    console.log('原始 tree 數據:', JSON.parse(JSON.stringify(treeList)))
    // 扁平化
    console.log('tree =>flat,扁平化後:', treeToFlat(JSON.parse(JSON.stringify(treeList)), flatList))
    // 反扁平化,SON.parse(JSON.stringify()) 為瞭實現深拷貝
    console.log('flat =>tree,反扁平化後:', flatToTree(JSON.parse(JSON.stringify(flatList)), treeList))
  }
  /**
   * 扁平化:將具有層級遞進關系結構的 tree 數據扁平化
   * 
   * @param treeList 有層級遞進關系結構的 tree 數據
   * @param flatList 用於接收扁平化結果的變量
   * @returns {*} 返回扁平化結果
   */
  function treeToFlat (treeList, flatList) {
    // flatList.length > 9999 是考慮底線保護原則,出於極限保護的目的設置的,可不設或按需設置。
    if (flatList.length > 9999) {
      return
    }
    treeList.map(e => {
      flatList.push(e)
      // 遞歸:有條件的自己調用自己,條件是 e.children.length 為真
      if (e.children && e.children.length) {
        treeToFlat(e.children, flatList)
      }
    })
    // console.log('扁平化後:', flatList)
    return flatList
  }
  /**
   * 反扁平化:將扁平結構的 flat 數據轉換為具有層級遞進關系結構的 tree 數據
   * 
   * @param flatList 扁平結構的數據
   * @param treeList 用於接收反扁平化結果的變量
   * @returns {*} 返回反扁平化結果
   */
  function flatToTree (flatList, treeList) {
    flatList.map(e => {
      // 以 e.pid===null,作為判斷是不是根節點的依據,或者直接寫死根節點(如果確定的話),
      // 具體以什麼作為判斷根節點的依據,得看數據的設計規則,通常是判斷層級或是否代表根節點的標記
      if (e.pid === null) {
        // 避免出現重復數據
        const index = treeList.findIndex(sub => sub.id === e.id)
        if (index === -1) {
          treeList.push(e)
        }
      }
      flatList.map(e2 => {
        if (e2.pid === e.id) {
          // 避免出現重復數據
          const index = e.children.findIndex(sub => sub.id === e2.id)
          if (index === -1) {
            e.children.push(e2)
          }
        }
      })
    })
    // console.log('反扁平化後:', treeList)
    return treeList
  }
</script>
</body>
</html>

三、寫在後面

這兩個扁平化與反扁平化寫法,感覺還有值得優化的方法,但暫時想不到。

此外,遞歸的應用也是值得註意的地方。

我理解的遞歸:有條件的自己調用自己

以上就是JavaScript前端面試扁平數據轉tree與tree數據扁平化的詳細內容,更多關於扁平數據轉tree數據扁平化的資料請關註WalkonNet其它相關文章!

推薦閱讀: