1、使用递归
缺点:算法复杂度稍大,O(n2);无法消除脏数据导致的循环引用问题(例如ID与parentId相同,但一般情况下都不会发生)。
export function list2treeV1(_list, _props = {}, _disabled = false) {
const props = {
// 标准字段映射
id: 'id', // id值使用字段,默认id
label: 'label', // 标签值字段,默认 label
disabled: 'disabled', // 是否禁用标记字段,默认 disabled
// 功能字段
parentId: 'parentId', // 父ID使用字段,默认children
children: 'children', // 子级结构集合字段,默认children
topParentId: null, // 顶级的parentId,默认null
// 自定义值覆盖
..._props
}
const list = _list
function _list2tree(prevList) {
prevList.forEach((tItem) => {
list.forEach((item) => {
if (item[props.parentId] === tItem[props.id]) {
if (!tItem[props.children]) tItem[props.children] = []
tItem[props.children].push(item)
}
})
if (tItem[props.children]?.length > 0) _list2tree(tItem[props.children])
})
}
const treeList = []
list.forEach((item) => {
// 赋标准字段值
item.id = item[props.id]
item.label = item[props.label]
item.disabled = item[props.disabled] || _disabled
if (!Reflect.has(item, props.parentId))
item[props.parentId] = props.topParentId
// 顶级列表
if (item[props.parentId] === props.topParentId) treeList.push(item)
})
_list2tree(treeList)
return treeList
}
2、使用map
优点:算法复杂度O(n),比V1版好
缺点:仍然无法消除脏数据引起的循环引用问题(例如ID与parentId相同,但一般情况下都不会发生)。
export function list2treeV2(_list, _props = {}, _disabled = false) {
const props = {
// 标准字段映射
id: 'id', // id值使用字段,默认id
label: 'label', // 标签值字段,默认 label
disabled: 'disabled', // 是否禁用标记字段,默认 disabled
// 功能字段
parentId: 'parentId', // 父ID使用字段,默认children
children: 'children', // 子级结构集合字段,默认children
topParentId: null, // 顶级的parentId,默认null
// 自定义值覆盖
..._props
}
const map = new Map()
_list.forEach((item) => {
// 赋标准字段值
item.id = item[props.id]
item.label = item[props.label]
item.disabled = item[props.disabled] || _disabled
if (!Reflect.has(item, props.parentId))
item[props.parentId] = props.topParentId
// 构造索引
const pId = item[props.parentId]
if (!map.has(pId)) {
map.set(pId, [])
}
map.get(pId).push(item)
})
_list.forEach((item) => {
const id = item[props.id]
if (map.has(id)) {
item[props.children] = map.get(id)
}
})
return map.get(props.topParentId)
}
3、使用递归,并消除循环引用。
消除循环引用的思路:将已添加到树结构中的项从列表中移除。
优点:可以消除循环引用。
缺点:算法复杂度和V1版一样稍大,为O(n2)。
export function list2treeV3(_list, _props = {}, _disabled = false) {
const props = {
// 标准字段映射
id: 'id', // id值使用字段,默认id
label: 'label', // 标签值字段,默认 label
disabled: 'disabled', // 是否禁用标记字段,默认 disabled
// 功能字段
parentId: 'parentId', // 父ID使用字段,默认children
children: 'children', // 子级结构集合字段,默认children
topParentId: null, // 顶级的parentId,默认null
// 自定义值覆盖
..._props
}
const list = deepClone(_list)
const treeList = []
list.forEach((item, index) => {
// 赋标准字段值
item.id = item[props.id]
item.label = item[props.label]
item.disabled = item[props.disabled] || _disabled
if (!Reflect.has(item, props.parentId))
item[props.parentId] = props.topParentId
// 顶级列表
if (item[props.parentId] === props.topParentId) {
treeList.push(...list.splice(index, 1, null)) // 已经添加到树结构中的项移除
}
})
function _list2tree(prevList) {
prevList.forEach((tItem) => {
list.forEach((item, index) => {
if (item && item[props.parentId] === tItem[props.id]) {
if (!tItem[props.children]) tItem[props.children] = []
tItem[props.children].push(...list.splice(index, 1, null))
}
})
if (tItem[props.children]?.length > 0) _list2tree(tItem[props.children]) // 已经添加到树结构中的项移除
})
}
_list2tree(treeList)
return treeList
}