N-dimensional tree is used to represent the UI of any application. Buttons, ComboBoxes and various other components are assembled together in this data structure and then rendered on the screen.
I had to enhance the design of a commercial Tree UI component (add improved searching features) and thought of creating my own n-ary Tree data structure to hold the data which will be passed into the Tree UI component.
class Node(object): def __init__(self, **kwargs): self.value = kwargs.get('value') self.children = [] def __str__(self): return 'Val: {0} Children: {1}'.format(self.value, self.children) def __hash__(self): return hash((self.value, self.children,)) __repr__ = __str__ class nAryTree(object): def __init__(self): self._root = Node() def getRoot(self): return self._root def insert(self, data): self._insertValues(data, self._root) def _insertValues(self, data, currNode): if not data: return if currNode and not currNode.value: tmpChild = None for child in currNode.children: if data[0] == child.value: tmpChild = child break if tmpChild: self._insertValues(data, tmpChild) else: newNode = Node(value=data[0]) currNode.children.append(newNode) self._insertValues(data, newNode) else: if len(data) > 1: tmpChild = None for child in currNode.children: if data[1] == child.value: tmpChild = child break if tmpChild: self._insertValues(data[1:], tmpChild) else: newNode = Node(value=data[1]) currNode.children.append(newNode) self._insertValues(data[1:], newNode) def show(self): self.showContainer = [] self._showTree(self._root) def _showTree(self, currNode): if currNode and not currNode.value: print '*Root' else: self.showContainer.append('--') self.showContainer.append(currNode.value) print ''.join(self.showContainer) self.showContainer.pop() self.showContainer.pop() if currNode and currNode.children: self.showContainer.append(' |') print ''.join(self.showContainer) for child in currNode.children: self._showTree(child) if currNode.children and self.showContainer: self.showContainer.pop() def deleteNode(self, tgt=''): searchContainer = [] result = {} res = self._search(self._root, tgt, searchContainer, None, result) parentNode = result.get('parentNode') for idx in xrange(len(parentNode.children)): if parentNode.children[idx].value == tgt: del parentNode.children[idx] break return searchContainer[1:] def searchTree(self, tgt=''): searchContainer = [] res = self._search(self._root, tgt, searchContainer) return searchContainer[1:] def _search(self, currNode, tgt, path=[], parentNodeOfTgt=None, result={}): if (not tgt and tgt.strip()) or not currNode: return False if currNode.value == tgt: path.append(currNode.value) result['parentNode'] = parentNodeOfTgt result['tgtNode'] = currNode print 'Path: {0}'.format('>'.join(path)) return True else: path.append(currNode.value if currNode.value else 'Root') for child in currNode.children: if self._search(child, tgt, path, currNode, result): return True else: path.pop() def nestedTuples(self): data = self._naryToNestedTuples(self._root) return data[1] if data else () def _naryToNestedTuples(self, t): return (t.value, tuple(map(self._naryToNestedTuples, t.children)), ) if t.children and isinstance(t.children, (tuple, list,)) \ else t.value def nestedLists(self): data = self._naryToNestedLists(self._root) return data[1] if data else [] def _naryToNestedLists(self, t): return [t.value, map(self._naryToNestedLists, t.children)] if t.children and isinstance(t.children, (tuple, list,)) \ else t.value def main(): tree = nAryTree() tree.insert(('A', 'B', 'C', 'D',)) tree.insert(('A', 'B', 'C', 'E',)) tree.insert(('A', 'B', 'F', 'X',)) tree.insert(('A', 'B', 'C', 'Y',)) tree.show() print '\nSearch result for {0}: {1}\n\n'.format('X', tree.searchTree('X')) print 'Accounting Organizational Structure' # http://www.chesterfield.gov/uploadedImages/Department_Information/Management_Services/Accounting/Media/Images/Accounting%20Organizational%20Structure.jpg?n=5909 tree2 = nAryTree() tree2.insert(('Director',)) tree2.insert(('Director', 'Financial Services & Grants Coordinators',)) tree2.insert(('Director', 'Assistant Director-I',)) tree2.insert(('Director', 'Assistant Director-I', 'Financial Systems Section',)) tree2.insert(('Director', 'Assistant Director-I', 'General Accounting Section',)) tree2.insert(('Director', 'Assistant Director-II',)) tree2.insert(('Director', 'Assistant Director-II', 'Accounts Payable Section',)) tree2.insert(('Director', 'Assistant Director-II', 'Payroll Section',)) tree2.insert(('Director', 'Assistant Director-II', 'Administration Section',)) tree2.show() print '\nSearch result for {0}: {1}\n\n'.format('Payroll Section', tree2.searchTree('Payroll Section')) raw_input() if __name__ == '__main__': main()
Output >>
*Root | |--A | | | |--B | | | | | |--C | | | | | | | |--D | | | |--E | | | |--Y | | |--F | | | | | | | |--X Path: Root>A>B>F>X Search result for X: ['A', 'B', 'F', 'X'] Accounting Organizational Structure *Root | |--Director | | | |--Financial Services & Grants Coordinators | |--Assistant Director-I | | | | | |--Financial Systems Section | | |--General Accounting Section | |--Assistant Director-II | | | | | |--Accounts Payable Section | | |--Payroll Section | | |--Administration Section Path: Root>Director>Assistant Director-II>Payroll Section Search result for Payroll Section: ['Director', 'Assistant Director-II', 'Payroll Section']
I struggled for several hours to find a Python tree that would accept duplicate items. Thank you for this “simple” and effective code.