vtils
Version:
一个面向业务的 JavaScript/TypeScript 实用程序库。
440 lines (416 loc) • 11.4 kB
JavaScript
import { cloneDeepFast } from "./cloneDeepFast.js";
/**
* 树数据处理。支持单根节点、多根节点树数据。
*/
export var TreeData = /*#__PURE__*/function () {
/**
* 构造函数。
*
* @param data 整棵树的数据
* @param options 选项
*/
function TreeData(data, options) {
if (options === void 0) {
options = {};
}
this.data = void 0;
this.childrenPropName = 'children';
this.searchStrategy = 'DFS';
this.cloneIgnore = undefined;
this.setOptions(options);
this.data = this.cloneDeep(Array.isArray(data) ? data : [data]);
}
var _proto = TreeData.prototype;
_proto.cloneDeep = function cloneDeep(value) {
return cloneDeepFast(value, this.cloneIgnore);
}
/**
* 核心遍历函数。
*/;
TreeData.traverse = function traverse(data, childrenPropName, searchStrategy, fn) {
var nodes = [];
for (var i = data.length - 1; i >= 0; i--) {
nodes.push([data[i], i, undefined, data, 0, []]);
}
var currentNode;
var removeNodes = [];
var isRemove = false;
var isExit = false;
var isSkipChildrenTraverse = false;
var removeNode = function removeNode() {
return isRemove = true;
};
var exit = function exit() {
return isExit = true;
};
var skipChildrenTraverse = function skipChildrenTraverse() {
return isSkipChildrenTraverse = true;
};
while (currentNode = nodes.pop()) {
var _currentNode = currentNode,
_node2 = _currentNode[0],
_index = _currentNode[1],
_parentNode = _currentNode[2],
_siblings = _currentNode[3],
_depth = _currentNode[4],
_path = _currentNode[5];
isRemove = false;
isExit = false;
isSkipChildrenTraverse = false;
fn({
node: _node2,
index: _index,
parentNode: _parentNode,
depth: _depth,
path: _path,
removeNode: removeNode,
exit: exit,
skipChildrenTraverse: skipChildrenTraverse
});
if (isRemove) {
if (!removeNodes.length || removeNodes[removeNodes.length - 1][0] !== _siblings) {
removeNodes.push([_siblings, []]);
}
removeNodes[removeNodes.length - 1][1].push(_index);
}
if (isExit) return;
if (!isRemove && !isSkipChildrenTraverse && _node2[childrenPropName] && Array.isArray(_node2[childrenPropName])) {
if (searchStrategy === 'DFS') {
for (var _i = _node2[childrenPropName].length - 1; _i >= 0; _i--) {
nodes.push([_node2[childrenPropName][_i], _i, _node2, _node2[childrenPropName], _depth + 1, _path.concat(_node2)]);
}
} else {
for (var _i2 = 0; _i2 < _node2[childrenPropName].length; _i2++) {
nodes.unshift([_node2[childrenPropName][_i2], _i2, _node2, _node2[childrenPropName], _depth + 1, _path.concat(_node2)]);
}
}
}
}
var _removeNode;
while (_removeNode = removeNodes.pop()) {
var removeNodeIndex = void 0;
while ((removeNodeIndex = _removeNode[1].pop()) != null) {
_removeNode[0].splice(removeNodeIndex, 1);
}
}
}
/**
* 设置选项。
*
* @param options 选项
*/;
_proto.setOptions = function setOptions(options) {
this.cloneIgnore = options.cloneIgnore || this.cloneIgnore;
this.childrenPropName = options.childrenPropName || this.childrenPropName;
this.searchStrategy = options.searchStrategy || this.searchStrategy;
return this;
}
/**
* 遍历节点。
*
* @param node 节点
* @param fn 遍历函数
* @param searchStrategy 遍历搜索方式,默认为选项中的遍历搜索方式
*/;
_proto.traverseNode = function traverseNode(node, fn, searchStrategy) {
if (searchStrategy === void 0) {
searchStrategy = this.searchStrategy;
}
var nodes = typeof node === 'function' ? this.findNodeAll(node) : Array.isArray(node) ? node : [node];
var _node;
while (_node = nodes.pop()) {
if (Array.isArray(_node[this.childrenPropName])) {
var fns = (Array.isArray(fn) ? fn : [fn]).filter(function (fn) {
return typeof fn === 'function';
});
for (var i = 0; i < fns.length; i++) {
TreeData.traverse(_node[this.childrenPropName], this.childrenPropName, searchStrategy, fns[i]);
}
}
}
return this;
}
/**
* 深度优先遍历节点。
*
* @param fn 遍历函数
*/;
_proto.traverseNodeDFS = function traverseNodeDFS(node, fn) {
return this.traverseNode(node, fn, 'DFS');
}
/**
* 广度优先遍历节点。
*
* @param fn 遍历函数
*/;
_proto.traverseNodeBFS = function traverseNodeBFS(node, fn) {
return this.traverseNode(node, fn, 'BFS');
}
/**
* 遍历。
*
* @param fn 遍历函数
* @param searchStrategy 遍历搜索方式,默认为选项中的遍历搜索方式
*/;
_proto.traverse = function traverse(fn, searchStrategy) {
var _this$traverseNode;
if (searchStrategy === void 0) {
searchStrategy = this.searchStrategy;
}
this.traverseNode((_this$traverseNode = {}, _this$traverseNode[this.childrenPropName] = this.data, _this$traverseNode), fn, searchStrategy);
return this;
}
/**
* 深度优先遍历。
*
* @param fn 遍历函数
*/;
_proto.traverseDFS = function traverseDFS(fn) {
return this.traverse(fn, 'DFS');
}
/**
* 广度优先遍历。
*
* @param fn 遍历函数
*/;
_proto.traverseBFS = function traverseBFS(fn) {
return this.traverse(fn, 'BFS');
}
/**
* 设置数据深度。从 `0` 开始,将会移除超过指定深度的数据。
*
* @param depth 深度
*/;
_proto.setDepth = function setDepth(depth) {
var _this = this;
this.traverse(function (payload) {
if (payload.depth === depth) {
delete payload.node[_this.childrenPropName];
}
});
return this;
}
/**
* 设置节点属性。
*
* @param props 节点属性键值映射对象,值为函数,用其返回值作为新的属性值
*/;
_proto.setNodeProps = function setNodeProps(props) {
this.traverse(function (payload) {
for (var propName in props) {
;
payload.node[propName] = props[propName](payload);
}
});
return this;
}
/**
* 移除节点上指定的属性。
*
* @param propNames 属性名列表
*/;
_proto.omitNodeProps = function omitNodeProps(propNames) {
this.traverse(function (payload) {
for (var i in propNames) {
delete payload.node[propNames[i]];
}
});
return this;
}
/**
* 选取节点上指定的属性。
*
* @param propNames 属性名列表
*/;
_proto.pickNodeProps = function pickNodeProps(propNames) {
this.traverse(function (payload) {
for (var propName in payload.node) {
if (propNames.indexOf(propName) === -1) {
delete payload.node[propName];
}
}
});
return this;
}
/**
* 筛选符合条件的节点。
*
* @param predicate 条件
*/;
_proto.filter = function filter(predicate) {
this.traverse([function (payload) {
if (predicate(payload)) {
;
payload.node.__SKIP__ = true;
payload.node.__PICK__ = true;
var _node3;
while (_node3 = payload.path.pop()) {
;
_node3.__PICK__ = true;
}
payload.skipChildrenTraverse();
}
}, function (payload) {
if (payload.node.__SKIP__ === true) {
payload.skipChildrenTraverse();
}
if (payload.node.__PICK__ !== true) {
payload.removeNode();
}
delete payload.node.__SKIP__;
delete payload.node.__PICK__;
}]);
return this;
}
/**
* 查找符合条件的第一个节点。
*
* @param predicate 条件
*/;
_proto.findNode = function findNode(predicate) {
var node;
this.traverse(function (payload) {
if (predicate(payload)) {
node = payload.node;
payload.exit();
}
});
return node;
}
/**
* 查找符合条件的所有节点。
*
* @param predicate 条件
*/;
_proto.findNodeAll = function findNodeAll(predicate) {
var nodes = [];
this.traverse(function (payload) {
if (predicate(payload)) {
nodes.push(payload.node);
}
});
return nodes;
}
/**
* 查找符合条件的第一个节点的路径。
*
* @param predicate 条件
*/;
_proto.findNodePath = function findNodePath(predicate) {
var path;
this.traverse(function (payload) {
if (predicate(payload)) {
path = payload.path.concat(payload.node);
payload.exit();
}
});
return path;
}
/**
* 查找符合条件的所有节点的路径。
*
* @param predicate 条件
*/;
_proto.findNodePathAll = function findNodePathAll(predicate) {
var paths = [];
this.traverse(function (payload) {
if (predicate(payload)) {
paths.push(payload.path.concat(payload.node));
}
});
return paths;
}
/**
* 移除符合条件的第一个节点。返回被移除的节点。
*
* @param predicate 条件
*/;
_proto.removeNode = function removeNode(predicate) {
var node;
this.traverse(function (payload) {
if (predicate(payload)) {
payload.removeNode();
node = payload.node;
payload.exit();
}
});
return node;
}
/**
* 移除符合条件的所有节点。返回被移除的节点组成的数组。
*
* @param predicate 条件
*/;
_proto.removeNodeAll = function removeNodeAll(predicate) {
var nodes = [];
this.traverse(function (payload) {
if (predicate(payload)) {
payload.removeNode();
nodes.push(payload.node);
}
});
return nodes;
}
/**
* 计算符合条件的节点个数。不给出条件则计算所有节点的个数。
*
* @param predicate 条件
*/;
_proto.count = function count(predicate) {
var counter = 0;
this.traverse(function (payload) {
if (predicate ? predicate(payload) : true) {
counter++;
}
});
return counter;
}
/**
* 克隆实例。
*/;
_proto.clone = function clone() {
return new TreeData(this.export());
}
/**
* 导出数据。
*/;
_proto.export = function _export() {
return this.cloneDeep(this.data);
}
/**
* 导出一维列表数据。
*/;
_proto.exportList = function exportList() {
var list = [];
this.traverse(function (payload) {
list.push(payload.node);
});
return this.cloneDeep(list);
}
/**
* 从一维列表生成实例。
*
* @param list 列表
* @param idKey ID 所在键
* @param parentIdKey 父 ID 所在键
*/;
TreeData.fromList = function fromList(list, idKey, parentIdKey) {
var itemMap = {};
for (var i = 0; i < list.length; i++) {
var item = list[i];
item.children = [];
itemMap[item[idKey]] = item;
}
var tree = [];
for (var _i3 = 0; _i3 < list.length; _i3++) {
var _item = list[_i3];
if (itemMap[_item[parentIdKey]] !== undefined) {
itemMap[_item[parentIdKey]].children.push(_item);
} else {
tree.push(_item);
}
}
return new TreeData(tree);
};
return TreeData;
}();