最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

使用JavaScript將扁平數(shù)據(jù)轉(zhuǎn)換為樹(shù)形結(jié)構(gòu)的多種實(shí)現(xiàn)方法

 更新時(shí)間:2025年04月30日 08:41:35   作者:北辰alk  
在實(shí)際開(kāi)發(fā)中,我們經(jīng)常需要將扁平的數(shù)據(jù)結(jié)構(gòu)(如數(shù)據(jù)庫(kù)查詢(xún)結(jié)果)轉(zhuǎn)換為樹(shù)形結(jié)構(gòu)(如菜單、評(píng)論回復(fù)等),本文將詳細(xì)介紹多種實(shí)現(xiàn)方法,并提供完整的代碼示例,需要的朋友可以參考下

一、理解數(shù)據(jù)結(jié)構(gòu)

1. 扁平數(shù)據(jù)結(jié)構(gòu)示例

const flatData = [
  { id: 1, name: '部門(mén)1', parentId: 0 },
  { id: 2, name: '部門(mén)2', parentId: 1 },
  { id: 3, name: '部門(mén)3', parentId: 1 },
  { id: 4, name: '部門(mén)4', parentId: 3 },
  { id: 5, name: '部門(mén)5', parentId: 3 },
  { id: 6, name: '部門(mén)6', parentId: 0 },
];

2. 期望的樹(shù)形結(jié)構(gòu)

[
  {
    id: 1,
    name: '部門(mén)1',
    children: [
      {
        id: 2,
        name: '部門(mén)2'
      },
      {
        id: 3,
        name: '部門(mén)3',
        children: [
          { id: 4, name: '部門(mén)4' },
          { id: 5, name: '部門(mén)5' }
        ]
      }
    ]
  },
  {
    id: 6,
    name: '部門(mén)6'
  }
]

二、實(shí)現(xiàn)方法

方法1:遞歸實(shí)現(xiàn)(經(jīng)典算法)

function buildTree(items, parentId = 0) {
  const result = [];
  
  for (const item of items) {
    if (item.parentId === parentId) {
      const children = buildTree(items, item.id);
      if (children.length) {
        item.children = children;
      }
      result.push(item);
    }
  }
  
  return result;
}

const treeData = buildTree(flatData);
console.log(JSON.stringify(treeData, null, 2));

優(yōu)點(diǎn)

  • 邏輯清晰直觀
  • 適合理解樹(shù)形結(jié)構(gòu)的構(gòu)建過(guò)程

缺點(diǎn)

  • 時(shí)間復(fù)雜度較高(O(n^2))
  • 大數(shù)據(jù)量時(shí)性能較差

方法2:使用對(duì)象引用(高效算法)

function buildTreeOptimized(items) {
  const itemMap = {};
  const result = [];
  
  // 首先構(gòu)建哈希映射
  for (const item of items) {
    itemMap[item.id] = { ...item, children: [] };
  }
  
  // 構(gòu)建樹(shù)結(jié)構(gòu)
  for (const item of items) {
    const node = itemMap[item.id];
    if (item.parentId === 0) {
      result.push(node);
    } else {
      if (itemMap[item.parentId]) {
        itemMap[item.parentId].children.push(node);
      }
    }
  }
  
  return result;
}

const optimizedTree = buildTreeOptimized(flatData);
console.log(JSON.stringify(optimizedTree, null, 2));

優(yōu)點(diǎn)

  • 時(shí)間復(fù)雜度O(n),性能優(yōu)異
  • 適合大數(shù)據(jù)量處理

缺點(diǎn)

  • 需要額外的內(nèi)存空間存儲(chǔ)映射

方法3:使用Map數(shù)據(jù)結(jié)構(gòu)(ES6+)

function buildTreeWithMap(items) {
  const map = new Map();
  const result = [];
  
  // 初始化所有節(jié)點(diǎn)并存入Map
  items.forEach(item => {
    map.set(item.id, { ...item, children: [] });
  });
  
  // 構(gòu)建樹(shù)結(jié)構(gòu)
  items.forEach(item => {
    const node = map.get(item.id);
    if (item.parentId === 0) {
      result.push(node);
    } else {
      const parent = map.get(item.parentId);
      if (parent) {
        parent.children.push(node);
      }
    }
  });
  
  return result;
}

const mapTree = buildTreeWithMap(flatData);
console.log(JSON.stringify(mapTree, null, 2));

優(yōu)點(diǎn)

  • 使用Map更現(xiàn)代,性能更好
  • 支持任意類(lèi)型作為鍵

方法4:使用reduce實(shí)現(xiàn)(函數(shù)式編程)

function buildTreeWithReduce(items) {
  const itemMap = items.reduce((map, item) => {
    map[item.id] = { ...item, children: [] };
    return map;
  }, {});
  
  return items.reduce((result, item) => {
    if (item.parentId === 0) {
      result.push(itemMap[item.id]);
    } else if (itemMap[item.parentId]) {
      itemMap[item.parentId].children.push(itemMap[item.id]);
    }
    return result;
  }, []);
}

const reducedTree = buildTreeWithReduce(flatData);
console.log(JSON.stringify(reducedTree, null, 2));

優(yōu)點(diǎn)

  • 函數(shù)式風(fēng)格,代碼簡(jiǎn)潔
  • 兩次遍歷完成構(gòu)建

三、進(jìn)階功能實(shí)現(xiàn)

1. 添加排序功能

function buildTreeWithSort(items, sortBy = 'id') {
  const itemMap = {};
  const result = [];
  
  // 構(gòu)建映射
  items.forEach(item => {
    itemMap[item.id] = { ...item, children: [] };
  });
  
  // 構(gòu)建樹(shù)結(jié)構(gòu)
  items.forEach(item => {
    const node = itemMap[item.id];
    if (item.parentId === 0) {
      result.push(node);
    } else if (itemMap[item.parentId]) {
      itemMap[item.parentId].children.push(node);
    }
  });
  
  // 遞歸排序
  function sortTree(nodes) {
    nodes.sort((a, b) => a[sortBy] - b[sortBy]);
    nodes.forEach(node => {
      if (node.children.length) {
        sortTree(node.children);
      }
    });
  }
  
  sortTree(result);
  return result;
}

const sortedTree = buildTreeWithSort(flatData, 'id');
console.log(JSON.stringify(sortedTree, null, 2));

2. 處理循環(huán)引用

function buildTreeWithCycleDetection(items) {
  const itemMap = {};
  const result = [];
  const visited = new Set();
  
  // 構(gòu)建映射
  items.forEach(item => {
    itemMap[item.id] = { ...item, children: [] };
  });
  
  // 檢測(cè)循環(huán)引用
  function hasCycle(node, parentIds = new Set()) {
    if (parentIds.has(node.id)) return true;
    parentIds.add(node.id);
    
    for (const child of node.children) {
      if (hasCycle(child, new Set(parentIds))) {
        return true;
      }
    }
    
    return false;
  }
  
  // 構(gòu)建樹(shù)結(jié)構(gòu)
  items.forEach(item => {
    if (!visited.has(item.id)) {
      const node = itemMap[item.id];
      if (item.parentId === 0) {
        result.push(node);
      } else if (itemMap[item.parentId]) {
        itemMap[item.parentId].children.push(node);
      }
      visited.add(item.id);
      
      // 檢查循環(huán)引用
      if (hasCycle(node)) {
        throw new Error(`檢測(cè)到循環(huán)引用,節(jié)點(diǎn)ID: ${node.id}`);
      }
    }
  });
  
  return result;
}

3. 支持自定義子節(jié)點(diǎn)字段名

function buildTreeCustomChildren(items, childrenField = 'children') {
  const itemMap = {};
  const result = [];
  
  // 構(gòu)建映射
  items.forEach(item => {
    itemMap[item.id] = { ...item, [childrenField]: [] };
  });
  
  // 構(gòu)建樹(shù)結(jié)構(gòu)
  items.forEach(item => {
    const node = itemMap[item.id];
    if (item.parentId === 0) {
      result.push(node);
    } else if (itemMap[item.parentId]) {
      itemMap[item.parentId][childrenField].push(node);
    }
  });
  
  return result;
}

四、性能優(yōu)化技巧

  • 使用Map代替普通對(duì)象:當(dāng)ID為非數(shù)字時(shí)性能更好
  • 批量處理數(shù)據(jù):對(duì)于大數(shù)據(jù)量可分批次處理
  • 使用Web Worker:對(duì)于極大數(shù)據(jù)集可在后臺(tái)線程處理
  • 惰性加載:只構(gòu)建和渲染可見(jiàn)部分的樹(shù)結(jié)構(gòu)

五、實(shí)際應(yīng)用示例

1. 渲染樹(shù)形菜單(React示例)

function TreeMenu({ data }) {
  return (
    <ul>
      {data.map(node => (
        <li key={node.id}>
          {node.name}
          {node.children && node.children.length > 0 && (
            <TreeMenu data={node.children} />
          )}
        </li>
      ))}
    </ul>
  );
}

// 使用
const treeData = buildTreeOptimized(flatData);
ReactDOM.render(<TreeMenu data={treeData} />, document.getElementById('root'));

2. 樹(shù)形表格(Vue示例)

<template>
  <table>
    <tbody>
      <tree-row 
        v-for="node in treeData" 
        :key="node.id" 
        :node="node"
        :level="0"
      />
    </tbody>
  </table>
</template>

<script>
import { buildTreeWithMap } from './treeUtils';

export default {
  data() {
    return {
      flatData: [...], // 原始扁平數(shù)據(jù)
      treeData: []
    };
  },
  created() {
    this.treeData = buildTreeWithMap(this.flatData);
  },
  components: {
    TreeRow: {
      props: ['node', 'level'],
      template: `
        <tr :style="{ paddingLeft: level * 20 + 'px' }">
          <td>{{ node.name }}</td>
          <td>{{ node.otherField }}</td>
        </tr>
        <tree-row 
          v-for="child in node.children" 
          :key="child.id" 
          :node="child"
          :level="level + 1"
          v-if="node.children"
        />
      `
    }
  }
};
</script>

六、總結(jié)與最佳實(shí)踐

  1. 選擇合適算法

    • 小數(shù)據(jù)量:遞歸算法簡(jiǎn)單直觀
    • 大數(shù)據(jù)量:對(duì)象引用或Map實(shí)現(xiàn)性能更好
  2. 處理邊界情況

    • 無(wú)效的parentId引用
    • 循環(huán)引用檢測(cè)
    • 重復(fù)數(shù)據(jù)處理
  3. 擴(kuò)展性考慮

    • 支持自定義子節(jié)點(diǎn)字段名
    • 添加排序功能
    • 支持異步數(shù)據(jù)加載
  4. 性能監(jiān)控

    • 對(duì)于超大數(shù)據(jù)集考慮分頁(yè)或虛擬滾動(dòng)
    • 使用性能分析工具監(jiān)控構(gòu)建時(shí)間
  5. 測(cè)試建議

// 單元測(cè)試示例
describe('樹(shù)形結(jié)構(gòu)構(gòu)建', () => {
  it('應(yīng)正確構(gòu)建樹(shù)形結(jié)構(gòu)', () => {
    const flatData = [...];
    const treeData = buildTreeOptimized(flatData);
    expect(treeData.length).toBe(2);
    expect(treeData[0].children.length).toBe(2);
    expect(treeData[0].children[1].children.length).toBe(2);
  });
  
  it('應(yīng)處理無(wú)效parentId', () => {
    const invalidData = [...];
    const treeData = buildTreeOptimized(invalidData);
    expect(treeData.length).toBe(1);
  });
});

通過(guò)本文介紹的各種方法和技巧,您應(yīng)該能夠根據(jù)實(shí)際需求選擇最適合的方式將扁平數(shù)據(jù)轉(zhuǎn)換為樹(shù)形結(jié)構(gòu)。在實(shí)際項(xiàng)目中,推薦使用對(duì)象引用或Map實(shí)現(xiàn)的高效算法,它們?cè)诖髷?shù)據(jù)量下表現(xiàn)優(yōu)異,同時(shí)代碼也相對(duì)清晰可維護(hù)。

以上就是使用JavaScript將扁平數(shù)據(jù)轉(zhuǎn)換為樹(shù)形結(jié)構(gòu)的多種實(shí)現(xiàn)方法的詳細(xì)內(nèi)容,更多關(guān)于JavaScript扁平數(shù)據(jù)轉(zhuǎn)樹(shù)形結(jié)構(gòu)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • JS調(diào)用打印方法設(shè)置頁(yè)眉頁(yè)腳的實(shí)例

    JS調(diào)用打印方法設(shè)置頁(yè)眉頁(yè)腳的實(shí)例

    一個(gè)網(wǎng)頁(yè)打印相關(guān)功能的擴(kuò)展演示特效,在實(shí)現(xiàn)了打印功能外,還實(shí)現(xiàn)了打印預(yù)覽、打印前的頁(yè)眉頁(yè)腳設(shè)置,直接打印等功能,以前對(duì)JS打印前設(shè)置頁(yè)腳見(jiàn)的不多,所以這一個(gè)也算是挺有價(jià)值的,希望閑暇時(shí)參閱
    2013-05-05
  • JavaScript中為元素加上name屬性的方法

    JavaScript中為元素加上name屬性的方法

    干前端這行當(dāng)已經(jīng)超過(guò)一個(gè)月了, 每天都會(huì)遇到新奇古怪, 甚至離奇的問(wèn)題. 雖然絕大部分都是一些小問(wèn)題, 但我覺(jué)得還是有必要記錄下來(lái)
    2011-05-05
  • ESC之ESC.wsf可以實(shí)現(xiàn)javascript的代碼壓縮附使用方法

    ESC之ESC.wsf可以實(shí)現(xiàn)javascript的代碼壓縮附使用方法

    可以對(duì)javascript的大小進(jìn)行壓縮。使javascript的加載速度變快。
    2007-05-05
  • 前端給后端傳數(shù)據(jù)的五種方式總結(jié)

    前端給后端傳數(shù)據(jù)的五種方式總結(jié)

    前端與后端數(shù)據(jù)交互是軟件開(kāi)發(fā)中不可或缺的一環(huán),下面這篇文章主要給大家介紹了關(guān)于前端給后端傳數(shù)據(jù)的五種方式,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2024-07-07
  • javascript瀏覽器用戶代理檢測(cè)腳本實(shí)現(xiàn)方法

    javascript瀏覽器用戶代理檢測(cè)腳本實(shí)現(xiàn)方法

    下面小編就為大家?guī)?lái)一篇javascript瀏覽器用戶代理檢測(cè)腳本實(shí)現(xiàn)方法。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2017-10-10
  • js中script的上下放置區(qū)別,Dom的增刪改創(chuàng)建操作實(shí)例分析

    js中script的上下放置區(qū)別,Dom的增刪改創(chuàng)建操作實(shí)例分析

    這篇文章主要介紹了js中script的上下放置區(qū)別,Dom的增刪改創(chuàng)建操作,結(jié)合實(shí)例形式分析了JavaScript基本dom事件、script在head和body中放置的區(qū)別、以及Dom的增刪改創(chuàng)建等相關(guān)操作技巧,需要的朋友可以參考下
    2019-12-12
  • JavaScript實(shí)現(xiàn)深拷貝的不同方法匯總

    JavaScript實(shí)現(xiàn)深拷貝的不同方法匯總

    在JavaScript中,我們經(jīng)常需要將一個(gè)對(duì)象的所有數(shù)據(jù)復(fù)制到另一個(gè)對(duì)象中,這種操作可以分為 淺拷貝 和 深拷貝,而深拷貝則是指完全復(fù)制一個(gè)對(duì)象及其所有嵌套的對(duì)象,而不僅僅是對(duì)象的引用,本文,我們將深入探討深拷貝的概念,以及如何使用不同的方法實(shí)現(xiàn)深拷貝
    2025-06-06
  • hash特點(diǎn)、hashchange事件介紹及其常見(jiàn)應(yīng)用場(chǎng)景

    hash特點(diǎn)、hashchange事件介紹及其常見(jiàn)應(yīng)用場(chǎng)景

    淺析hash特點(diǎn)、hashchange事件介紹及其常見(jiàn)應(yīng)用場(chǎng)景(不同hash對(duì)應(yīng)不同事件處理、移動(dòng)端大圖展示狀態(tài)后退頁(yè)面問(wèn)題、原生輕應(yīng)用頭部后退問(wèn)題、移動(dòng)端自帶返回按鈕二次確認(rèn)問(wèn)題),hashchange和popstate事件觸發(fā)條件
    2023-11-11
  • 一文總結(jié)JS中邏輯運(yùn)算符的特點(diǎn)

    一文總結(jié)JS中邏輯運(yùn)算符的特點(diǎn)

    在JavaScript的眾多運(yùn)算符里,提供了三個(gè)邏輯運(yùn)算符&&、||和!,下面這篇文章主要給大家介紹了關(guān)于JS中邏輯運(yùn)算符的特點(diǎn),文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2022-03-03
  • layui使用button按鈕 點(diǎn)擊出現(xiàn)彈層 彈層中加載表單的實(shí)例

    layui使用button按鈕 點(diǎn)擊出現(xiàn)彈層 彈層中加載表單的實(shí)例

    今天小編就為大家分享一篇layui使用button按鈕 點(diǎn)擊出現(xiàn)彈層 彈層中加載表單的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-09-09

最新評(píng)論

磴口县| 遵义县| 平远县| 宜兰市| 江源县| 叶城县| 化德县| 宝山区| 雅安市| 桦南县| 师宗县| 运城市| 来凤县| 铁岭县| 新泰市| 兴仁县| 广灵县| 宜都市| 泾川县| 凤冈县| 界首市| 怀安县| 高安市| 阳高县| 堆龙德庆县| 大埔县| 新泰市| 特克斯县| 镇平县| 永顺县| 潜山县| 临西县| 堆龙德庆县| 泰顺县| 北辰区| 全南县| 沐川县| 乐山市| 龙门县| 榕江县| 昌乐县|