UNPKG

@lukeaus/plain-tree

Version:

A plain tree with a bunch of tree tools

478 lines (465 loc) 16.6 kB
'use strict'; var nodeData = function (node) { return node && 'data' in node ? node.data : node; }; var nodesData = function (nodes) { return nodes.map(nodeData); }; var hasChildren = function (node) { return Boolean(node && node.children && node.children.length); }; var generateChars = function (length) { var random11Chars = function () { return Math.random().toString(36).substring(2, 15); }; var chars = ''; while (chars.length < length) { chars += random11Chars(); } return chars.slice(0, length); }; var generateId = function () { return generateChars(36); }; var firstArrayElement = function (arr) { return Array.isArray(arr) && arr.length ? arr[0] : null; }; var filterObject = function (obj, _a) { var _b = _a.disallowedKeys, disallowedKeys = _b === void 0 ? [] : _b; var indexed = obj; var filteredObj = Object.keys(indexed) .filter(function (key) { return !disallowedKeys.includes(key); }) .reduce(function (o, key) { o[key] = indexed[key]; return o; }, {}); return filteredObj; }; var nodeToJsonFormatter = function (node) { var parent = node.parent, data = node.data, children = node.children, id = node.id; var obj = { data: data, children: children, id: id, parentId: null }; parent && (obj.parentId = parent.id); obj.children = node.children.map(function (child) { return nodeToJsonFormatter(child); }); return obj; }; var widthsByHeight = function (node) { var _a; if (node === null) { return [1]; } else { var counter = [1]; var currentQueue = [node]; var nextQueue = []; do { while (currentQueue.length) { var node_1 = currentQueue.pop(); hasChildren(node_1) && nextQueue.push.apply(nextQueue, node_1.children); } if (nextQueue.length) { counter[counter.length] = nextQueue.length; } _a = [currentQueue, nextQueue], nextQueue = _a[0], currentQueue = _a[1]; } while (currentQueue.length); return counter; } }; var flattenByHeight = function (node, fn) { var _a; if (fn === void 0) { fn = null; } var currentQueue = [node]; var nextQueue = []; var result = [[fn(node)]]; do { while (currentQueue.length) { var node_2 = currentQueue.pop(); hasChildren(node_2) && nextQueue.push.apply(nextQueue, node_2.children); } if (nextQueue.length) { if (fn) { result[result.length] = nextQueue.map(function (node) { return fn(node); }); } else { result[result.length] = nextQueue; } } _a = [currentQueue, nextQueue], nextQueue = _a[0], currentQueue = _a[1]; } while (currentQueue.length); return result; }; var Tree = (function () { function Tree(root) { if (root === void 0) { root = null; } this.root = root; this.root = root; } Tree.prototype._traverse = function (fn, _a, queueMethod) { var _b = _a === void 0 ? {} : _a, some = _b.some, every = _b.every, returnBoolean = _b.returnBoolean, returnArray = _b.returnArray; var queue = [this.root]; var results = []; var didBreak = false; var lastResult; while (queue.length) { var node = queue.shift(); hasChildren(node) && queue[queueMethod].apply(queue, node.children); if (some || every) { var result = fn(node); if (result && returnArray) { results.push(node); } if ((every && !result) || (some && result)) { didBreak = true; lastResult = result; break; } } else { fn(node); } } if (every) { if (returnBoolean) { return !didBreak; } else if (returnArray) { return results; } } else if (some) { if (returnBoolean) { return Boolean(lastResult); } else if (returnArray) { return results; } } }; Tree.prototype._traverseBreathFirst = function (fn, opts) { return this._traverse(fn, opts, 'push'); }; Tree.prototype._traverseDepthFirst = function (fn, opts) { return this._traverse(fn, opts, 'unshift'); }; Tree.prototype.traverseBreathFirst = function (fn) { this._traverseBreathFirst(fn); }; Tree.prototype.traverseDepthFirst = function (fn) { this._traverseDepthFirst(fn); }; Tree.prototype.someBreathFirst = function (fn) { return Boolean(this._traverseBreathFirst(fn, { some: true, returnBoolean: true })); }; Tree.prototype.someDepthFirst = function (fn) { return Boolean(this._traverseDepthFirst(fn, { some: true, returnBoolean: true })); }; Tree.prototype.everyBreathFirst = function (fn) { return Boolean(this._traverseBreathFirst(fn, { every: true, returnBoolean: true })); }; Tree.prototype.everyDepthFirst = function (fn) { return Boolean(this._traverseDepthFirst(fn, { every: true, returnBoolean: true })); }; Tree.prototype.findOneBreathFirst = function (fn) { var result = this._traverseBreathFirst(fn, { some: true, returnArray: true }); return firstArrayElement(result); }; Tree.prototype.findOneDepthFirst = function (fn) { var result = this._traverseDepthFirst(fn, { some: true, returnArray: true }); return firstArrayElement(result); }; Tree.prototype.findAllBreathFirst = function (fn) { var result = this._traverseBreathFirst(fn, { every: true, returnArray: true }); return Array.isArray(result) ? result : []; }; Tree.prototype.findAllDepthFirst = function (fn) { var result = this._traverseDepthFirst(fn, { every: true, returnArray: true }); return Array.isArray(result) ? result : []; }; Tree.prototype.flatMap = function (fn) { if (fn === void 0) { fn = null; } var acc = []; this._traverseBreathFirst(function (node) { (fn && acc.push(fn(node))) || acc.push(node); }); return acc; }; Tree.prototype.flattenData = function () { return this.flatMap(nodeData); }; Tree.prototype.flattenByHeight = function (fn) { if (fn === void 0) { fn = null; } return flattenByHeight(this.root, fn); }; Tree.prototype.flattenDataByHeight = function () { return this.flattenByHeight(nodeData); }; Tree.prototype.widthsByHeight = function () { return widthsByHeight(this.root); }; Tree.prototype.nodesAtHeight = function (height) { var _a; var counter = this.root ? [1] : []; var currentQueue = [this.root]; if (counter.length === height) { return currentQueue; } var nextQueue = []; do { while (currentQueue.length) { var node = currentQueue.pop(); hasChildren(node) && nextQueue.push.apply(nextQueue, node.children); } if (counter.length === height) { return nextQueue; } else { if (nextQueue.length) { counter[counter.length] = nextQueue.length; } _a = [currentQueue, nextQueue], nextQueue = _a[0], currentQueue = _a[1]; } } while (currentQueue.length); return []; }; Tree.prototype.countNodes = function () { return this.widthsByHeight().reduce(function (acc, curr) { return acc + curr; }, 0); }; Tree.prototype.maxWidth = function () { return Math.max.apply(Math, this.widthsByHeight()); }; Tree.prototype.height = function () { return this.root ? this.root.height() : 0; }; Tree.prototype.toJson = function () { return this.root ? this.root.toJson() : ''; }; return Tree; }()); var Node = (function () { function Node(data, _a) { var _b = _a === void 0 ? {} : _a, id = _b.id, parent = _b.parent; this.children = []; this.id = id !== undefined ? id : generateId(); this.parent = parent || null; this.data = data; this.children = []; } Node.prototype.addChild = function (data, _a) { var _b = _a === void 0 ? {} : _a, id = _b.id; var node = new Node(data, { id: id, parent: this }); this.children.push(node); return node; }; Node.prototype._removeChildren = function (fn) { var removedChildren = []; this.children = this.children.filter(function (node) { if (fn(node)) { removedChildren.push(node); return false; } return true; }); return removedChildren; }; Node.prototype.removeChildren = function (fn) { return this._removeChildren(fn); }; Node.prototype.removeChildrenByData = function (data) { var fn = function (node) { return node.data === data; }; return this._removeChildren(fn); }; Node.prototype.removeChildrenById = function (id) { var fn = function (node) { return node.id === id; }; return this._removeChildren(fn); }; Node.prototype.isLeaf = function () { return this.parent !== null && !Boolean(this.children.length); }; Node.prototype.hasChildren = function () { return Boolean(this.children.length); }; Node.prototype.toJson = function () { var objectToSerialize = nodeToJsonFormatter(this); return JSON.stringify(objectToSerialize); }; Node.prototype.depth = function () { if (!this.parent) { return 0; } else { var depth = 0; var currentNode = this; while (currentNode.parent) { depth += 1; currentNode = currentNode.parent; } return depth; } }; Node.prototype.widthsByHeight = function () { return widthsByHeight(this); }; Node.prototype.height = function () { return this.widthsByHeight().length - 1; }; Node.prototype.flattenByHeight = function (fn) { if (fn === void 0) { fn = null; } return flattenByHeight(this, fn); }; return Node; }()); /****************************************************************************** Copyright (c) Microsoft Corporation. Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ***************************************************************************** */ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */ function __spreadArray(to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); } typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) { var e = new Error(message); return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e; }; var ID_KEY_DEFAULT = 'id'; var PARENT_ID_KEY_DEFAULT = 'parentId'; var CHILDREN_KEY_DEFAULT = 'children'; var createTreeArrayFromFlatArray = function (data, _a) { var _b = _a === void 0 ? {} : _a, _c = _b.idKey, idKey = _c === void 0 ? ID_KEY_DEFAULT : _c, _d = _b.parentIdKey, parentIdKey = _d === void 0 ? PARENT_ID_KEY_DEFAULT : _d, _e = _b.childrenKey, childrenKey = _e === void 0 ? CHILDREN_KEY_DEFAULT : _e; var treeArray = []; var childrenOf = {}; data.forEach(function (obj) { var id = obj[idKey]; var parentId = obj[parentIdKey]; childrenOf[id] = childrenOf[id] || []; obj[childrenKey] = childrenOf[id]; if (parentId) { childrenOf[parentId] = childrenOf[parentId] || []; childrenOf[parentId].push(obj); } else { treeArray.push(obj); } }); return treeArray; }; var objectToNode = function (obj, parent, _a) { if (parent === void 0) { parent = null; } var _b = _a === void 0 ? {} : _a, _c = _b.idKey, idKey = _c === void 0 ? ID_KEY_DEFAULT : _c, _d = _b.parentIdKey, parentIdKey = _d === void 0 ? PARENT_ID_KEY_DEFAULT : _d, _e = _b.childrenKey, childrenKey = _e === void 0 ? CHILDREN_KEY_DEFAULT : _e; var indexed = obj; var disallowedKeys = [idKey, parentIdKey, childrenKey]; var data = filterObject(obj, { disallowedKeys: disallowedKeys }); if (parent) { return parent.addChild(data, { id: indexed[idKey] }); } else { return new Node(data, { id: indexed[idKey] }); } }; var createNodes = function (data, parentNode, opts) { if (parentNode === void 0) { parentNode = null; } if (opts === void 0) { opts = {}; } if (!data.length) { return; } var _a = opts.childrenKey, childrenKey = _a === void 0 ? CHILDREN_KEY_DEFAULT : _a; data.forEach(function (obj) { var node = objectToNode(obj, parentNode, opts); createNodes(obj[childrenKey], node, opts); }); }; var createTreeFromTreeArray = function (data, opts) { if (opts === void 0) { opts = {}; } if (!data.length) { return new Tree(); } else if (data.length > 1) { throw new Error('Converting an array to tree only accepts an array with 0 or 1 node currently'); } var _a = opts.childrenKey, childrenKey = _a === void 0 ? CHILDREN_KEY_DEFAULT : _a; var rootObj = data[0]; var root = objectToNode(rootObj, null, opts); var tree = new Tree(root); createNodes(rootObj[childrenKey], root, opts); return tree; }; var mapFlatArray = function (data, _a) { var _b = _a === void 0 ? {} : _a, idKey = _b.idKey, parentIdKey = _b.parentIdKey; if (idKey || parentIdKey) { var disallowedKeys_1 = __spreadArray(__spreadArray([], (idKey ? [idKey] : []), true), (parentIdKey ? [parentIdKey] : []), true); return data.map(function (obj) { var newObj = filterObject(obj, { disallowedKeys: disallowedKeys_1 }); idKey && (newObj[ID_KEY_DEFAULT] = obj[idKey]); parentIdKey && (newObj[PARENT_ID_KEY_DEFAULT] = obj[parentIdKey]); return newObj; }); } return data; }; var createTreeFromFlatArray = function (data, opts) { if (opts === void 0) { opts = {}; } var mappedFlatArray = mapFlatArray(data, opts); var treeArray = createTreeArrayFromFlatArray(mappedFlatArray); if (!treeArray.length) { return new Tree(); } else if (treeArray.length === 1) { return createTreeFromTreeArray(treeArray); } else { throw new Error('Converting an array to tree only accepts an array with 0 or 1 node currently'); } }; exports.Node = Node; exports.Tree = Tree; exports.createNodes = createNodes; exports.createTreeArrayFromFlatArray = createTreeArrayFromFlatArray; exports.createTreeFromFlatArray = createTreeFromFlatArray; exports.createTreeFromTreeArray = createTreeFromTreeArray; exports.filterObject = filterObject; exports.firstArrayElement = firstArrayElement; exports.generateId = generateId; exports.hasChildren = hasChildren; exports.nodeData = nodeData; exports.nodesData = nodesData; exports.objectToNode = objectToNode;