js-tree-toolkit
v1.0.3
Published
js树处理工具类
Downloads
2
Readme
数组转树
function arrayToTree(array, parentIdKey = 'parentId', childKey = 'children') {
const tree = [];
const map = {};
array.forEach(item => map[item.id] = item);
array.forEach(item => {
const parent = map[item[parentIdKey]];
if (parent) {
(parent[childKey] || (parent[childKey] = [])).push(item);
} else {
tree.push(item);
}
});
return tree;
}
测试代码
const data = [
{ id: 1, pId: null, name: 'A' },
{ id: 2, pId: 1, name: 'B' },
{ id: 3, pId: 1, name: 'C' },
{ id: 4, pId: 2, name: 'D' },
{ id: 5, pId: null, name: 'E' }
];
const tree = arrayToTree(data, 'pId', 'subItems');
树转数组
function treeToArray(tree, parentIdKey = 'parentId', childKey = 'children') {
const array = [];
const stack = [...tree];
while (stack.length > 0) {
const node = stack.pop();
const item = { ...node };
delete item[childKey];
array.push(item);
if (node[childKey]) {
node[childKey].forEach(child => {
child[parentIdKey] = node.id;
stack.push(child);
});
}
}
return array;
}
测试代码
const tree = [
{
id: 1,
name: 'A',
children: [
{
id: 2,
name: 'B',
children: [
{
id: 4,
name: 'D'
}
]
},
{
id: 3,
name: 'C'
}
]
},
{
id: 5,
name: 'E'
}
];
const array = treeToArray(tree);
console.log(array);
树遍历的方法
function traverseTree(tree, callback, childKey = 'children') {
if (!Array.isArray(tree)) {
return 'Invalid tree data: tree must be an array';
}
if (typeof callback !== 'function') {
return 'Invalid callback: callback must be a function';
}
for (const node of tree) {
callback(node);
if (node[childKey]) {
if (!Array.isArray(node[childKey])) {
return `Invalid tree data: ${childKey} must be an array`;
}
const result = traverseTree(node[childKey], callback, childKey);
if (result) {
return result;
}
}
}
}
测试代码
const tree = [
{
id: 1,
name: 'A',
children: [
{
id: 2,
name: 'B',
children: [
{
id: 4,
name: 'D'
}
]
},
{
id: 3,
name: 'C'
}
]
},
{
id: 5,
name: 'E'
}
];
const result = traverseTree(tree, node => console.log(node.name));
if (result) {
console.error(result);
}
js tree修复方法
function fixTree(tree, childKey = 'children') {
if (!Array.isArray(tree)) {
return;
}
tree.forEach(node => {
if (!Array.isArray(node[childKey])) {
node[childKey] = [];
}
fixTree(node[childKey], childKey);
});
}
测试代码
const tree = [
{
id: 1,
name: 'A',
children: [
{
id: 2,
name: 'B',
children: [
{
id: 4,
name: 'D'
}
]
},
{
id: 3,
name: 'C'
}
]
},
{
id: 5,
name: 'E',
children: 'invalid'
}
];
fixTree(tree);
console.log(tree);
获取树最大深度方法
function getMaxDepth(tree, childKey = 'children') {
if (!Array.isArray(tree) || tree.length === 0) {
return 0;
}
let maxDepth = 0;
tree.forEach(node => {
maxDepth = Math.max(maxDepth, getMaxDepth(node[childKey], childKey));
});
return maxDepth + 1;
}
测试代码
const tree = [
{
id: 1,
name: 'A',
children: [
{
id: 2,
name: 'B',
children: [
{
id: 4,
name: 'D'
}
]
},
{
id: 3,
name: 'C'
}
]
},
{
id: 5,
name: 'E'
}
];
const maxDepth = getMaxDepth(tree);
console.log(maxDepth);
获取根节点到当前节点数据的方法
function getSubTree(tree, findNodeFunc, childKey = 'children', includeCurrentNode = true) {
let subTree = null;
for (let i = 0; i < tree.length; i++) {
if (findNodeFunc(tree[i])) {
return includeCurrentNode ? tree[i] : null;
}
if (tree[i][childKey]) {
subTree = getSubTree(tree[i][childKey], findNodeFunc, childKey, includeCurrentNode);
if (subTree) {
return { ...tree[i], [childKey]:[subTree] };
}
}
}
return subTree;
}
测试数据
const tree = [
{
id: 1,
children: [
{
id: 2,
children: [
{
id: 4
},
{
id: 5
}
]
},
{
id: 3
}
]
}
];
const subTree = getSubTree(tree, node => node.id === 5);
console.log(subTree);
/*
{
id: 1,
children: [
{
id: 2,
children: [
{
id: 5
}
]
}
]
}
*/
获取兄弟节点方法
function getSiblingNodes(tree, nodeFunc, childKey = 'children', includeCurrentNode = false) {
let siblings = [];
let parent = null;
let currentNode = null;
let findParentAndCurrentNode = (tree) => {
if (Array.isArray(tree)) {
for (let i = 0; i < tree.length; i++) {
if (nodeFunc(tree[i])) {
parent = tree;
currentNode = tree[i];
return;
} else {
findParentAndCurrentNode(tree[i]);
}
}
} else if (tree[childKey]) {
for (let i = 0; i < tree[childKey].length; i++) {
if (nodeFunc(tree[childKey][i])) {
parent = tree;
currentNode = tree[childKey][i];
return;
} else {
findParentAndCurrentNode(tree[childKey][i]);
}
}
}
}
findParentAndCurrentNode(tree);
if (parent) {
siblings = parent[childKey].filter(n => includeCurrentNode || n !== currentNode);
}
return siblings;
}
测试方法
let tree = {
name: 'root',
children: [
{
name: 'a',
children: [
{ name: 'a1' },
{ name: 'a2' }
]
},
{
name: 'b',
children: [
{ name: 'b1' },
{ name: 'b2' }
]
}
]
};
let nodeFunc = (node) => node.name === 'a1';
let siblings = getSiblingNodes(tree, nodeFunc);
console.log(siblings); // 输出:[ { name: 'a2' } ]
获取子节点方法
function getChildren(node, filterFn, childKey = 'children', includeCurrent = true) {
let result = [];
if (includeCurrent && filterFn(node)) {
result.push(node);
}
if (node[childKey]) {
node[childKey].forEach(child => {
result = result.concat(getChildren(child, filterFn, childKey, includeCurrent));
});
}
return result;
}
测试代码
const tree = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 3
},
{
id: 4
}
]
},
{
id: 5,
children: [
{
id: 6
}
]
}
]
};
console.log(getChildren(tree, node => node.id == 2));
获取叶子节点方法
function getLeafNodes(node, childKey = 'children') {
let result = [];
if (!node[childKey] || node[childKey].length === 0) {
result.push(node);
} else {
node[childKey].forEach(child => {
result = result.concat(getLeafNodes(child, childKey));
});
}
return result;
}
测试代码
const tree = {
id: 1,
subNodes: [
{
id: 2,
subNodes: [
{
id: 3
},
{
id: 4
}
]
},
{
id: 5,
subNodes: [
{
id: 6
}
]
}
]
};
console.log(getLeafNodes(tree, 'subNodes'));
遍历树叶子节点
function traverseLeafNodes(node, callback, childKey = 'children') {
if (!node[childKey] || node[childKey].length === 0) {
callback(node);
} else {
node[childKey].forEach(child => {
traverseLeafNodes(child, callback, childKey);
});
}
}
测试代码
const tree = {
id: 1,
subNodes: [
{
id: 2,
subNodes: [
{
id: 3
},
{
id: 4
}
]
},
{
id: 5,
subNodes: [
{
id: 6
}
]
}
]
};
traverseLeafNodes(tree, node => console.log(node.id), 'subNodes');
设置树的层级和是否叶子节点
function setLeafNodesAndLevel(nodeOrNodes, level = 1, childKey = 'children') {
if (Array.isArray(nodeOrNodes)) {
// 如果参数是数组,则处理每个节点
nodeOrNodes.forEach(node => {
node.level = level;
if (!node[childKey] || node[childKey].length === 0) {
node.isLeaf = true;
} else {
node.isLeaf = false;
setLeafNodesAndLevel(node[childKey], level + 1, childKey);
}
});
} else if (typeof nodeOrNodes === 'object') {
// 如果参数是单个对象,则处理该对象
nodeOrNodes.level = level;
if (!nodeOrNodes[childKey] || nodeOrNodes[childKey].length === 0) {
nodeOrNodes.isLeaf = true;
} else {
nodeOrNodes.isLeaf = false;
setLeafNodesAndLevel(nodeOrNodes[childKey], level + 1, childKey);
}
} else {
throw new Error('Invalid input. Expected an object or an array.');
}
}
测试代码
//入参为对象
const tree = {
id: 1,
subNodes: [
{
id: 2,
subNodes: [
{
id: 3
},
{
id: 4
}
]
},
{
id: 5,
subNodes: [
{
id: 6
}
]
}
]
};
setLeafNodesAndLevel(tree, 0, 'subNodes');
console.log(tree);
//入参是数组
const inputArray = [
{
name: 'Node 1',
children: [
{
name: 'Node 1.1',
children: [
{
name: 'Node 1.1.1',
children: []
},
{
name: 'Node 1.1.2',
children: []
}
]
},
{
name: 'Node 1.2',
children: []
}
]
},
{
name: 'Node 2',
children: [
{
name: 'Node 2.1',
children: []
},
{
name: 'Node 2.2',
children: []
}
]
}
];
// 执行函数
setLeafNodesAndLevel(inputArray);
获取指定层级父节点的方法 默认获取父亲
function getParentNode(tree, filterFn, childKey = 'children') {
if (!tree || typeof tree !== 'object') {
return null;
}
function searchParentNode(node, parent) {
if (filterFn(node)) {
return parent;
}
if (Array.isArray(node[childKey])) {
for (const child of node[childKey]) {
const result = searchParentNode(child, node);
if (result) {
return result;
}
}
}
return null;
}
return searchParentNode(tree, null);
}
测试代码
// 示例用法:
const tree = {
name: 'A',
children: [
{
name: 'B',
children: [
{
name: 'C',
children: [
{
name: 'D',
children: [
{
name: 'E',
},
],
},
],
},
],
},
],
};
const filterFn = (node) => node.name === 'C';
const parentNode = getParentNode(tree, filterFn);
console.log(parentNode); // 输出: { name: 'B', children: [...] }
计算叶子节点的个数
function countLeafNodes(node, childKey = 'children') {
if (!node[childKey] || node[childKey].length === 0) {
return 1;
} else {
let count = 0;
node[childKey].forEach(child => {
count += countLeafNodes(child, childKey);
});
return count;
}
}
测试代码
const tree = {
id: 1,
subNodes: [
{
id: 2,
subNodes: [
{
id: 3
},
{
id: 4
}
]
},
{
id: 5,
subNodes: [
{
id: 6
}
]
}
]
};
console.log(countLeafNodes(tree, 'subNodes'));
// 输出: 3
把js tree的层级补充到指定层级
function fillTree(tree, depth, fillFunction, childKey = 'children') {
if (Array.isArray(tree)) {
tree.forEach(item => fillTree(item, depth, fillFunction, childKey));
} else {
if (depth < 1) return;
if (!tree[childKey]) tree[childKey] = [];
if (tree[childKey].length === 0) tree[childKey].push(fillFunction());
tree[childKey].forEach(child => fillTree(child, depth - 1, fillFunction, childKey));
}
}
测试代码
let tree = {
value: 1,
children: [
{
value: 2,
children: [
{
value: 3
}
]
},
{
value: 4
}
]
};
fillTree(tree, 3, () => ({value: 0}));
console.log(JSON.stringify(tree, null, 2));
获取数组中对应的子节点
function getArrayChildren(data, filterFn = () => true, includeCurrent = true, pIdKey = 'pId', idKey = 'id') {
let result = [];
let current = data.find(filterFn);
if (current && includeCurrent) result.push(current);
const getChildrenRecursively = (parentId) => {
data.forEach(item => {
if (item[pIdKey] === parentId && filterFn(item)) {
result.push(item);
getChildrenRecursively(item[idKey]);
}
});
};
if (current) getChildrenRecursively(current[idKey]);
return result;
}
测试代码
const data = [
{ id: 1, pId: null, name: 'Node 1' },
{ id: 2, pId: 1, name: 'Node 1.1' },
{ id: 3, pId: 1, name: 'Node 1.2' },
{ id: 4, pId: 2, name: 'Node 1.1.1' },
{ id: 5, pId: 2, name: 'Node 1.1.2' },
{ id: 6, pId: null, name: 'Node 2' },
{ id: 7, pId: 6, name: 'Node 2.1' },
{ id: 8, pId: 6, name: 'Node 2.2' },
];
const result = getArrayChildren(data, item => item.name.includes('Node 1'), true, 'pId', 'id');
console.log(result);
获取数组中父节点的方法
function getArrayParent(data, filterFn = () => true, includeCurrent = true, pIdKey = 'parentId', idKey = 'id') {
let result = [];
let current = data.find(filterFn);
if (current && includeCurrent) result.push(current);
while (current && current[pIdKey] !== null) {
current = data.find(item => item[idKey] === current[pIdKey]);
if (current) {
result.push(current);
}
}
return result;
}
测试代码
const data = [
{ id: 1, parentId: null, name: 'Node 1' },
{ id: 2, parentId: 1, name: 'Node 1.1' },
{ id: 3, parentId: 1, name: 'Node 1.2' },
{ id: 4, parentId: 2, name: 'Node 1.1.1' },
{ id: 5, parentId: 2, name: 'Node 1.1.2' },
{ id: 6, parentId: null, name: 'Node 2' },
{ id: 7, parentId: 6, name: 'Node 2.1' },
{ id: 8, parentId: 6, name: 'Node 2.2' },
];
const result = getArrayParent(data, item => item.name.includes('Node 1'), true, 'parentId', 'id');
console.log(result);
树节点移动方法
function moveTreeNode(treeData, sourceNodeFilterFn, targetNodeFilterFn, childKey = 'children') {
let sourceNode = null;
let targetNode = null;
let findNodesRecursively = (data) => {
data.forEach(item => {
if (sourceNodeFilterFn(item)) sourceNode = item;
if (targetNodeFilterFn(item)) targetNode = item;
if (item[childKey]) findNodesRecursively(item[childKey]);
});
}
findNodesRecursively(treeData);
if (!sourceNode || !targetNode) return;
let removeSourceNodeRecursively = (data) => {
data.forEach((item, index) => {
if (item === sourceNode) data.splice(index, 1);
else if (item[childKey]) removeSourceNodeRecursively(item[childKey]);
});
}
removeSourceNodeRecursively(treeData);
if (!targetNode[childKey]) targetNode[childKey] = [];
targetNode[childKey].push(sourceNode);
}
测试数据
let treeData = [
{
id: 1,
name: 'A',
children: [
{id: 2, name: 'B'},
{id: 3, name: 'C'}
]
},
{
id: 4,
name: 'D',
children: [
{id: 5, name: 'E'},
{id: 6, name: 'F'}
]
}
];
moveTreeNode(treeData, item => item.name === 'B', item => item.name === 'F');
console.log(JSON.stringify(treeData));
查找指定节点方法
function findNode(tree, filterFn, childKey = 'children') {
if (Array.isArray(tree)) {
for (let node of tree) {
let result = findNode(node, filterFn, childKey);
if (result) {
return result;
}
}
} else {
if (filterFn(tree)) {
return tree;
}
if (tree[childKey]) {
for (let child of tree[childKey]) {
let result = findNode(child, filterFn, childKey);
if (result) {
return result;
}
}
}
}
return null;
}
测试数据
// 定义树结构
const tree = {
id: 1,
children: [
{
id: 2,
children: [
{
id: 3,
name: 'Node 3'
},
{
id: 4,
name: 'Node 4'
}
]
},
{
id: 5,
children: [
{
id: 6,
name: 'Node 6'
}
]
}
]
};
// 测试条件:查找 id 为 3 的节点
const resultNode = findNode(tree, node => node.id === 3, 'children');
if (resultNode) {
console.log('找到节点:', resultNode);
} else {
console.log('未找到节点。');
}
删除树指定节点
function deleteNodes(tree, filterFn) {
if (Array.isArray(tree)) {
for (let i = tree.length - 1; i >= 0; i--) {
if (filterFn(tree[i])) {
tree.splice(i, 1);
} else if (typeof tree[i] === 'object') {
deleteNodes(tree[i], filterFn);
}
}
} else if (typeof tree === 'object') {
for (const key in tree) {
if (tree.hasOwnProperty(key) && typeof tree[key] === 'object') {
deleteNodes(tree[key], filterFn);
}
}
}
}
测试数据
// 示例用法:
const tree = {
name: 'A',
children: [
{
name: 'B',
children: [
{
name: 'C',
children: [
{
name: 'D',
children: [
{
name: 'E',
},
],
},
],
},
],
},
],
};
const filterFn = (node) => node.name === 'E';
deleteNodes(tree, filterFn);
console.log(JSON.stringify(tree, null, 2));
修改树节点信息
function modifyNodes(tree, filterFn, callback, childKey = 'children') {
if (Array.isArray(tree)) {
for (let i = 0; i < tree.length; i++) {
if (filterFn(tree[i])) {
tree[i] = callback(tree[i]);
}
if (tree[i][childKey] && Array.isArray(tree[i][childKey])) {
modifyNodes(tree[i][childKey], filterFn, callback, childKey);
}
}
} else if (typeof tree === 'object') {
if (tree[childKey] && Array.isArray(tree[childKey])) {
modifyNodes(tree[childKey], filterFn, callback, childKey);
}
}
}
测试代码
// 示例用法:
const tree = {
name: 'A',
children: [
{
name: 'B',
children: [
{
name: 'C',
children: [
{
name: 'D',
children: [
{
name: 'E',
},
],
},
],
},
],
},
],
};
const filterFn = (node) => node.name === 'C' || node.name === 'E';
const callback = (node) => {
// 修改节点数据
return {
...node,
name: node.name + '_Modified',
};
};
modifyNodes(tree, filterFn, callback);
console.log(JSON.stringify(tree, null, 2));