# 树形相关方法
树形数组在很多情况下都会使用,下面将树形相关方法汇总起来。
# 树形数组层级数据
const areaTreeData = [
{
"id": "35060000",
"areaName": "漳州市",
"children": [
{
"id": "35062400",
"areaName": "诏安县",
"children": [
{
"id": "35062401",
"areaName": "太平镇",
},
{
"id": "35062402",
"areaName": "金星乡",
},
{
"id": "35062403",
"areaName": "南诏镇",
},
{
"id": "35062404",
"areaName": "霞葛镇",
}
],
"leaf": false
},
{
"id": "35062300",
"areaName": "云霄县",
"children": [
{
"id": "35062301",
"areaName": "列屿镇",
},
{
"id": "35062302",
"areaName": "陈岱镇",
},
{
"id": "35062303",
"areaName": "云陵镇",
},
{
"id": "35062304",
"areaName": "火田镇",
}
],
}
],
}
]
# 树形数组一维数据
const areaArrData = [
{
"id": "35060000",
"parentId": "0",
"areaName": "漳州市"
},
{
"id": "35062400",
"parentId": "35060000",
"areaName": "诏安县"
},
{
"id": "35062401",
"parentId": "35062400",
"areaName": "太平镇"
},
{
"id": "35062402",
"parentId": "35062400",
"areaName": "金星乡"
},
{
"id": "35062403",
"parentId": "35062400",
"areaName": "南诏镇"
},
{
"id": "35062404",
"parentId": "35062400",
"areaName": "霞葛镇"
},
{
"id": "35062300",
"parentId": "35060000",
"areaName": "云霄县"
},
{
"id": "35062301",
"parentId": "35062300",
"areaName": "列屿镇"
},
{
"id": "35062302",
"parentId": "35062300",
"areaName": "陈岱镇"
},
{
"id": "35062303",
"parentId": "35062300",
"areaName": "云陵镇"
},
{
"id": "35062304",
"parentId": "35062300",
"areaName": "火田镇"
}
]
# 方法汇总
# 方法一:将一维树形数据转化为层级树形数据
- **listToTree **简洁版为
export function listToTree(data, parentId) {
var tree = [];
var temp;
// 遍历数组
for (var i = 0; i < data.length; i++) {
// 如果这个元素的 父id 是要找的
if (data[i].parentId === parentId) {
var obj = data[i];
// 查找这个元素的子元素
temp = listToTree(data, obj.id)
if (temp.length > 0) {
obj.children = temp
}
tree.push(obj);
}
}
return tree;
}
- **listToTree **升级版本,使之变成可配置并且每项会新增字段
export function listToTree(data, config, parentObj) {
const defaultOptions = {
idField: "id", //唯一标识字段名
parentIdField: "parentId", //父节点唯一标识字段名
childrenField: "children", //子节点标识字段名
firstId: "0", // 根节点值
labelField: "label", //label字段名
labelArrField: "labelArr", //给对象新增的中文数组字段名
idArrField: "idArr", //给对象新增的id数组字段名
targetArrField: "", //目标字段数组 多个可以用 , 分隔开
levelField: "level", //给对象新增的层级字段名
level: 0, // 给根目录配置的层级
leafField: "leaf" //叶子节点标识字段名
}
Object.assign(defaultOptions, config)
if (!parentObj) {
parentObj = {
[defaultOptions.idField]: defaultOptions.firstId,
[defaultOptions.levelField]: defaultOptions.level,
[defaultOptions.labelArrField]: [],
[defaultOptions.idArrField]: [],
}
}
var tree = [];
var temp;
// 遍历数组
for (var i = 0; i < data.length; i++) {
// 如果这个元素的 父id 是要找的
if (data[i][defaultOptions.parentIdField] === parentObj[defaultOptions.idField]) {
var obj = data[i];
// 给元素添加层级标志
obj[defaultOptions.levelField] = parentObj[defaultOptions.levelField] + 1
// 给元素添加 字段数组
obj[defaultOptions.labelArrField] = parentObj[defaultOptions.labelArrField].concat(obj[defaultOptions.labelField])
// 给元素添加 id数组
obj[defaultOptions.idArrField] = parentObj[defaultOptions.idArrField].concat(obj[defaultOptions.idField])
// 查找这个元素的子元素
temp = listToTree(data, config, obj)
if (temp.length > 0) {
obj[defaultOptions.childrenField] = temp
obj[defaultOptions.leafField] = false
} else {
// 没有子元素 说明是叶子节点
obj[defaultOptions.leafField] = true
}
tree.push(obj);
}
}
return tree;
}
# 方法二:树形层级数据,根据ID查询父级元素路径
- getPath( reduce方法) 初级版
const getPath = (arr, id) => {
return arr.reduce((total, item) => {
if (item.id == id) {
total.push(item)
} else {
if (item.children && item.children.length) {
let path = getPath(item.children, id)
if (path && path.length) {
total.unshift(item, ...path)
}
}
}
return total
}, [])
}
- getPath( reduce方法) 升级成可配置
const getPath = (arr, targetValue, config) => {
const defaultConfig = {
targetField: "id", // 查找目标值的字段名
childrenField: "children" //子节点标识字段名
}
Object.assign(defaultConfig, config)
return arr.reduce((total, item) => {
if (item[defaultConfig.targetField] === targetValue) {
total.push(item)
} else {
if (item[defaultConfig.childrenField] && item[defaultConfig.childrenField].length) {
const path = getPath(item[defaultConfig.childrenField], targetValue, config)
if (path && path.length) {
total.unshift(item, ...path)
}
}
}
return total
}, [])
}
- getPath( for方法) 初级版
const getPath = (arr, id) => {
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
if (item.id == id) {
return [item]
} else if (item.children && item.children.length) {
const path = getPath(item.children, id)
if (path && path.length) {
path.unshift(item)
return path
}
}
}
}
- getPath( for方法) 升级版
const getPath = (arr, targetValue, config ) => {
const defaultConfig = {
targetField: "id", // 查找目标值的字段名
childrenField: "children" //子节点标识字段名
}
Object.assign(defaultConfig, config)
for (let i = 0; i < arr.length; i++) {
const item = arr[i]
if (item[defaultConfig.targetField] === targetValue) {
return [item]
} else if (item[defaultConfig.childrenField] && item[defaultConfig.childrenField].length) {
const path = getPath(item[defaultConfig.childrenField], targetValue, config)
if (path && path.length) {
path.unshift(item)
return path
}
}
}
}
# 方法三:树形层级数据转化为以为数组结构
- 获取树形数据所有叶子节点
/**
* @description: 获取所有叶子节点
* @return {*}
* @author: syx
*/
export function treeToList(treeArr) {
var r = []
if (Array.isArray(treeArr)) {
for (var i = 0, l = treeArr.length; i < l; i++) {
const item = treeArr[i]
if (Array.isArray(item.children) && item.children.length > 0) {
// 若存在children则递归调用,把数据拼接到新数组中,并且删除该children
r = r.concat(treeToList(item.children))
} else {
r.push(item) // 取每项数据放入一个新数组
}
delete item.children
}
}
return r
}
- 获取树形数据所有元素
/**
* @description: 获取所有叶子节点
* @return {*}
* @author: syx
*/
export function treeToList(treeArr) {
var r = []
if (Array.isArray(treeArr)) {
for (var i = 0, l = treeArr.length; i < l; i++) {
const item = treeArr[i]
if (Array.isArray(item.children) && item.children.length > 0) {
r.push(item)
// 若存在children则递归调用,把数据拼接到新数组中,并且删除该children
r = r.concat(treeToList(item.children))
} else {
r.push(item) // 取每项数据放入一个新数组
}
delete item.children
}
}
return r
}
- 获取树形数据所有元素并且添加pid字段
/**
* @description: 获取树形数据所有元素并且添加pid字段
* @return {*}
* @author: syx
*/
export function treeToList(treeArr, parentId = "0") {
var r = []
if (Array.isArray(treeArr)) {
for (var i = 0, l = treeArr.length; i < l; i++) {
const item = treeArr[i]
item.pid = parentId
if (Array.isArray(item.children) && item.children.length > 0) {
r.push(item)
// 若存在children则递归调用,把数据拼接到新数组中,并且删除该children
r = r.concat(treeToList(item.children, item.id))
} else {
r.push(item) // 取每项数据放入一个新数组
}
delete item.children
}
}
return r
}
- 获取树形数据所有元素并且添加level层级字段
/**
* @description: 获取树形数据所有元素并且添加level层级字段
* @return {*}
* @author: syx
*/
export function treeToList(treeArr, level = 0) {
var r = []
if (Array.isArray(treeArr)) {
for (var i = 0, l = treeArr.length; i < l; i++) {
const item = treeArr[i]
item.level = level + 1
if (Array.isArray(item.children) && item.children.length > 0) {
r.push(item)
// 若存在children则递归调用,把数据拼接到新数组中,并且删除该children
r = r.concat(treeToList(item.children, item.level))
} else {
r.push(item) // 取每项数据放入一个新数组
}
delete item.children
}
}
return r
}
- 升级成可配置树形转数组方法
/**
* @description: 可配置树形转一维数组
* @return {*}
* @author: syx
*/
export function treeToList(treeArr, config, rootObj) {
const defaultOptions = {
idField: "id", //唯一标识字段名
parentIdField: "parentId", //父节点唯一标识字段名
childrenField: "children", //子节点标识字段名
firstId: "0", // 根节点值
labelField: "label", //label字段名
labelArrField: "labelArr", //给对象新增的中文数组字段名
idArrField: "idArr", //给对象新增的id数组字段名
targetArrField: "", //目标字段数组 多个可以用 , 分隔开
levelField: "level", //给对象新增的层级字段名
level: 0, // 给根目录配置的层级
leafField: "leaf" //叶子节点标识字段名
}
Object.assign(defaultOptions, config)
// 没值说明是根目录
if (!rootObj) {
rootObj = {
[defaultOptions.idField]: defaultOptions.firstId, //自身id
[defaultOptions.levelField]: defaultOptions.level, // 自身层级
[defaultOptions.labelArrField]: [], // 自身labelArr
[defaultOptions.idArrField]: [], // 自身labelArr
}
}
var r = []
if (Array.isArray(treeArr)) {
for (var i = 0, l = treeArr.length; i < l; i++) {
const item = treeArr[i]
// 层级
item[defaultOptions.levelField] = rootObj[defaultOptions.levelField] + 1
// 父元素id
item[defaultOptions.parentIdField] = rootObj[defaultOptions.idField]
// label数组
item[defaultOptions.labelArrField] = rootObj[defaultOptions.labelArrField].concat([item[defaultOptions.labelField]])
// id数组
item[defaultOptions.idArrField] = rootObj[defaultOptions.idArrField].concat([item[defaultOptions.idField]])
if (Array.isArray(item.children) && item.children.length > 0) {
// 不是叶子节点
item[defaultOptions.leafField] = false
r.push(item)
// 若存在children则递归调用,把数据拼接到新数组中,并且删除该children
r = r.concat(treeToList(item.children,config, item))
} else {
// 叶子节点
item[defaultOptions.leafField] = true
r.push(item) // 取每项数据放入一个新数组
}
delete item.children
}
}
return r
}
# 方法四:树形层级数据过滤部分数据
- 树形数据过滤id为35062300的数据
// 定义递归方法,接收一个数组
function deepFilter(list, exceptId) {
// 使用filter 过滤当前层的数组
return list.filter(item => {
// filter其实也是遍历
// 把当前遍历的节点的children 也调用一次 deepFilter 函数,返回过滤后的数组重新赋值
if (item.children&&item.children.length > 0) {
item.children = deepFilter(item.children, exceptId)
}
// 最后判断当前节点是否符合过滤要求
return item.id !== exceptId
})
}
// 过滤掉id为35062300 的数据 子元素也会 过滤
deepFilter(areaTreeData, "35062300")
- 升级成可灵活使用的函数
// 定义递归方法,接收一个数组
function deepFilter(list, callback) {
// 使用filter 过滤当前层的数组
return list.filter(item => {
// filter其实也是遍历
// 把当前遍历的节点的children 也调用一次 deepFilter 函数,返回过滤后的数组重新赋值
if (item.children&&item.children.length > 0) {
item.children = deepFilter(item.children, callback)
}
// 最后判断当前节点是否符合过滤要求
return callback&&callback(item)
})
}
deepFilter(areaTreeData, (item) => {return item.id !== "35062300"})
- 可配置children字段
// 定义递归方法,接收一个数组
function deepFilter(list, callback, childrenField="children") {
// 使用filter 过滤当前层的数组
return list.filter(item => {
// filter其实也是遍历
// 把当前遍历的节点的children 也调用一次 deepFilter 函数,返回过滤后的数组重新赋值
if (item[childrenField]&&item[childrenField].length > 0) {
item[childrenField] = deepFilter(item[childrenField], callback)
}
// 最后判断当前节点是否符合过滤要求
return callback&&callback(item)
})
}
deepFilter(areaTreeData, (item) => {return item.id !== "35062300"})
# 总结
树形转一维数组,一维数组转树形,上述方法希望能帮到大家。