type ItemTree = { data: number | undefined count: number children: Array } function treeInit(): ItemTree { return { data: undefined, count: 0, children: [] } } function treeNode(data: number, count: number): ItemTree { return { data: data, count: count, children: [] } } /* * Merge a node with an item tree */ function nodeMerge(itemtree: ItemTree, node: ItemTree) { const item = node.data const count = node.count let next: ItemTree | null = null // Try to find an existing node in this tree level with same item for (const node of itemtree.children) { if (node.data == item) { node.count += 1 next = node break } } // If not found, add item node at this level if (next == null) { next = treeNode(item, count) itemtree.children.push(next) } return next } /* * Merge a full build path with an existing item tree */ function treeMerge(itemtree: ItemTree, items: Array) { let current = itemtree for (const item of items) { current = nodeMerge(current, { data: item, count: 1, children: [] }) } } function treeCutBranches(itemtree: ItemTree, thresholdCount: number, thresholdPerc: number) { // Remove branches that are above threshold count while (itemtree.children.length > thresholdCount) { const leastUsedBranch = itemtree.children.reduce( (a, b) => (Math.min(a.count, b.count) == a.count ? a : b), { data: undefined, count: +Infinity, children: [] } ) itemtree.children.splice(itemtree.children.indexOf(leastUsedBranch), 1) } // Remove branches that are of too low usage const toRemove: Array = [] for (const child of itemtree.children) { if (child.count / itemtree.count < thresholdPerc) { toRemove.push(child) } } for (const tr of toRemove) { itemtree.children.splice(itemtree.children.indexOf(tr), 1) } itemtree.children.map(x => treeCutBranches(x, thresholdCount, thresholdPerc)) } function treeSort(itemtree: ItemTree) { itemtree.children.sort((a, b) => b.count - a.count) for (const item of itemtree.children) { treeSort(item) } } export { ItemTree, treeMerge, treeInit, treeCutBranches, treeSort }