UNPKG

vtils

Version:

一个面向业务的 JavaScript/TypeScript 实用程序库。

440 lines (416 loc) 11.4 kB
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; }();