亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

使用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ù)查詢結(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)代,性能更好
  • 支持任意類型作為鍵

方法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)文章

最新評(píng)論