你還在循環(huán)嵌套評論列表?教你一招把評論數(shù)據(jù)變成 Tree 結(jié)構(gòu)!
在開發(fā)評論系統(tǒng)時,我們經(jīng)常會拿到“扁平化”的評論數(shù)據(jù),比如:
[
{"id": 1, "parent_id": null, "content": "頂級評論"},
{"id": 2, "parent_id": 1, "content": "這是對1的回復"},
{"id": 3, "parent_id": 1, "content": "我也是對1的回復"},
{"id": 4, "parent_id": 2, "content": "回復2"},
{"id": 5, "parent_id": null, "content": "另一個頂級評論"}
]
這種結(jié)構(gòu)通常來自數(shù)據(jù)庫查詢,邏輯上是“父子關(guān)系”,但前端展示評論樓層時更希望拿到:
[
{
"id": 1,
"content": "...",
"children": [
{
"id": 2,
"content": "...",
"children": [
{
"id": 4,
"content": "...",
"children": []
}
]
},
{
"id": 3,
"content": "...",
"children": []
}
]
},
{
"id": 5,
"content": "...",
"children": []
}
]
也就是所謂的“樹形結(jié)構(gòu)”。那么問題來了:
如何將一組帶有 parent_id 的平鋪數(shù)據(jù)結(jié)構(gòu),轉(zhuǎn)換成嵌套的樹形結(jié)構(gòu)?
思路拆解
思路非常清晰,但你得理解三個關(guān)鍵步驟:
- 建立映射表:將所有數(shù)據(jù)按 id 索引,方便查找每一條記錄。
- 遍歷數(shù)據(jù):根據(jù) parent_id 判斷每一條記錄的“父節(jié)點”。
- 掛載子節(jié)點:如果找到了父節(jié)點,把自己加入它的 children 列表;如果沒有,就作為“頂級節(jié)點”。
完整代碼實現(xiàn)(可直接運行)
from typing import List, Dict, Any
import json
def build_comment_tree(comments: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
"""
將扁平化評論列表轉(zhuǎn)換為樹形結(jié)構(gòu)
:param comments: 包含 id 和 parent_id 的評論列表
:return: 嵌套的評論樹
"""
# 初始化映射表,key 為 id,value 是評論內(nèi)容 + 空的 children
comment_map = {
comment["id"]: {**comment, "children": []}
for comment in comments
}
tree = []
for comment in comment_map.values():
parent_id = comment.get("parent_id")
if parent_id is None:
# 頂級評論,放到根列表中
tree.append(comment)
else:
# 子評論,找到它的父節(jié)點并掛載
parent = comment_map.get(parent_id)
if parent:
parent["children"].append(comment)
else:
# parent_id 找不到對應評論,可選處理方式
tree.append(comment) # 或記錄異常日志
return tree
# 示例評論數(shù)據(jù)
if __name__ == "__main__":
comment_list = [
{"id": 1, "parent_id": None, "content": "頂級評論"},
{"id": 2, "parent_id": 1, "content": "這是對1的回復"},
{"id": 3, "parent_id": 1, "content": "我也是對1的回復"},
{"id": 4, "parent_id": 2, "content": "回復2"},
{"id": 5, "parent_id": None, "content": "另一個頂級評論"}
]
tree_result = build_comment_tree(comment_list)
# 打印格式化的結(jié)果
print(json.dumps(tree_result, ensure_ascii=False, indent=2))
運行結(jié)果展示(部分)
[
{
"id": 1,
"parent_id": null,
"content": "頂級評論",
"children": [
{
"id": 2,
"parent_id": 1,
"content": "這是對1的回復",
"children": [
{
"id": 4,
"parent_id": 2,
"content": "回復2",
"children": []
}
]
},
{
"id": 3,
"parent_id": 1,
"content": "我也是對1的回復",
"children": []
}
]
},
{
"id": 5,
"parent_id": null,
"content": "另一個頂級評論",
"children": []
}
]
常見應用場景
- 評論/回復樓層嵌套顯示
- 組織架構(gòu)樹(部門-員工)
- 欄目分類目錄(如博客、CMS)
- 后臺權(quán)限菜單(父子菜單結(jié)構(gòu))
可拓展功能建議
- 加入排序字段(比如按時間或點贊數(shù)排序)
- 限制最大層級,避免無限遞歸(防止性能問題)
- 構(gòu)建 ID → 子節(jié)點 的倒排索引,提高構(gòu)建效率(適合超大數(shù)據(jù))
小結(jié)
將扁平結(jié)構(gòu)轉(zhuǎn)為樹形結(jié)構(gòu),在評論系統(tǒng)、后臺管理系統(tǒng)中非常常見,這段代碼簡潔高效,可直接應用于實際項目。
建議你將這段邏輯封裝成一個工具模塊或類,在多個項目中復用,省時省力!