Ruchit
Ruchit

Reputation: 681

Convert node tree to dictionary

Below is the node class definition with tree structure (built using node instance) of programming languages. Now how to convert hierarchical node data tree structure to python dictionary using node class method ? See desired output at bottom.

class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent

        if parent is not None:
            parent.addChild(self)

    def addChild(self, child):
        self._children.append(child)

    def name(self):
        return self._name

    def setName(self, name):
        self._name = name

    def child(self, row):
        return self._children[row]

    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent


rootNode   = nodeData.Node("books")
web_node = nodeData.Node("web", rootNode)
database_node = nodeData.Node("database", rootNode)
front_end_node = nodeData.Node("front-end", web_node)
back_end_node = nodeData.Node("back-end", web_node)
sql_node = nodeData.Node("sql", database_node)
nosql_node = nodeData.Node("nosql", database_node)
html_node = nodeData.Node("html", front_end_node)
css_node = nodeData.Node("css", front_end_node)
js_node = nodeData.Node("js", front_end_node)
php_node = nodeData.Node("php", back_end_node)
python_node = nodeData.Node("python", back_end_node)
mysql_node = nodeData.Node("mysql", sql_node)
postgresql_node = nodeData.Node("postgresql", sql_node)
mongodb_node = nodeData.Node("mongodb", nosql_node)
cassandra_node = nodeData.Node("cassandra", nosql_node)
html_book_one_node = nodeData.Node("the missing manual", html_node)
html_book_two_node = nodeData.Node("core html5 canvas", html_node)
css_book_one_node = nodeData.Node("css pocket reference", css_node)
css_book_two_node = nodeData.Node("css in depth", css_node)
js_book_one_node = nodeData.Node("you don't know js", js_node)
js_book_two_node = nodeData.Node("eloquent javascript", js_node)
php_book_one_node = nodeData.Node("modern php", php_node)
python_book_one_node = nodeData.Node("dive into python", python_node)
python_book_two_node = nodeData.Node("python for everybody", python_node)
python_book_three_node = nodeData.Node("Think Python", python_node)
mongodb_book_one_node = nodeData.Node("mongodb in action", mongodb_node)
mongodb_two_node = nodeData.Node("scaling mongodb", mongodb_node)

Output

From node tree abstraction to python dictionary

{"books":{ "web":{ "front-end":{ "html":["the missing manual", "core html5 canvas"], "css":["css pocket reference", "css in depth"], "js":["you don't know js", "eloquent javascript"] }, "back-end":{ "php":["modern php"], "python":["dive into python", "python for everybody", "Think Python"] } }, "database":{ "sql":{ "mysql":[], "postgresql":[] }, "nosql":{ "mongodb":["mongodb in action", "scaling mongodb"], "cassandra":[] }}}}

Updated Code

class Node(object):
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent

        if parent is not None:
            parent.addChild(self)

    def addChild(self, child):
        self._children.append(child)

    def name(self):
        return self._name

    def setName(self, name):
        self._name = name

    def child(self, row):
        return self._children[row]

    def childCount(self):
        return len(self._children)

    def parent(self):
        return self._parent

class categoryNode(Node):
    def __init__(self, name, parent=None):
        super(categoryNode, self).__init__(name, parent)

class subCategoryNode(Node):
    def __init__(self, name, parent=None):
        super(subCategoryNode, self).__init__(name, parent)

class languageNode(Node):
    def __init__(self, name, parent=None):
        super(languageNode, self).__init__(name, parent)

class BookNode(Node):
    def __init__(self, name, parent=None):
        super(BookNode, self).__init__(name, parent)


rootNode   = Node("books")
web_node = categoryNode("web", rootNode)
database_node = categoryNode("database", rootNode)
front_end_node = subCategoryNode("front-end", web_node)
back_end_node = subCategoryNode("back-end", web_node)
sql_node = subCategoryNode("sql", database_node)
nosql_node = subCategoryNode("nosql", database_node)
html_node = languageNode("html", front_end_node)
css_node = languageNode("css", front_end_node)
js_node = languageNode("js", front_end_node)
php_node = languageNode("php", back_end_node)
python_node = languageNode("python", back_end_node)
mysql_node = languageNode("mysql", sql_node)
postgresql_node = languageNode("postgresql", sql_node)
mongodb_node = languageNode("mongodb", nosql_node)
cassandra_node = languageNode("cassandra", nosql_node)
html_book_one_node = BookNode("the missing manual", html_node)
html_book_two_node = BookNode("core html5 canvas", html_node)
css_book_one_node = BookNode("css pocket reference", css_node)
css_book_two_node = BookNode("css in depth", css_node)
js_book_one_node = BookNode("you don't know js", js_node)
js_book_two_node = BookNode("eloquent javascript", js_node)
php_book_one_node = BookNode("modern php", php_node)
python_book_one_node = BookNode("dive into python", python_node)
python_book_two_node = BookNode("python for everybody", python_node)
python_book_three_node = BookNode("Think Python", python_node)
mongodb_book_one_node = BookNode("mongodb in action", mongodb_node)
mongodb_two_node = BookNode("scaling mongodb", mongodb_node)

Upvotes: 3

Views: 938

Answers (2)

kaya3
kaya3

Reputation: 51162

You have a bigger problem, because you're using the same class to represent both book categories and actual books. Given this, it is impossible to programmatically determine that mysql_node and postgresql_node are empty categories rather than books.

To make this work how you want, you will need to redesign the data structure. I suggest having a list _children for subcategories and another list _books for book titles (as strings). Note that this data structure is still a little ambiguous because a node with no subcategories and no books could be rendered as an empty list (i.e. a terminal category with no books) or an empty dictionary (i.e. a non-terminal category with no subcategories); I infer from the question that an empty list is the desired result.

class Node:
    def __init__(self, name, parent=None):
        self._name = name
        self._children = []
        self._parent = parent
        self._books = []

        if parent is not None:
            parent.addChild(self)

    def name(self):
        return self._name

    def setName(self, name):
        self._name = name

    def parent(self):
        return self._parent

    def addChild(self, child):
        if self._books:
            raise ValueError('Node cannot have both sub-categories and books')
        self._children.append(child)

    def child(self, row):
        return self._children[row]

    def childCount(self):
        return len(self._children)

    def addBook(self, book):
        if self._children:
            raise ValueError('Node cannot have both sub-categories and books')
        self._books.append(book)

    def book(self, row):
        return self._books[row]

    def bookCount(self):
        return len(self._books)

rootNode = Node("books")
web_node = Node("web", rootNode)
database_node = Node("database", rootNode)
front_end_node = Node("front-end", web_node)
back_end_node = Node("back-end", web_node)
sql_node = Node("sql", database_node)
nosql_node = Node("nosql", database_node)
html_node = Node("html", front_end_node)
css_node = Node("css", front_end_node)
js_node = Node("js", front_end_node)
php_node = Node("php", back_end_node)
python_node = Node("python", back_end_node)
mysql_node = Node("mysql", sql_node)
postgresql_node = Node("postgresql", sql_node)
mongodb_node = Node("mongodb", nosql_node)
cassandra_node = Node("cassandra", nosql_node)

html_node.addBook("the missing manual")
html_node.addBook("core html5 canvas")
css_node.addBook("css pocket reference")
css_node.addBook("css in depth")
js_node.addBook("you don't know js")
js_node.addBook("eloquent javascript")
php_node.addBook("modern php")
python_node.addBook("dive into python")
python_node.addBook("python for everybody")
python_node.addBook("Think Python")
mongodb_node.addBook("mongodb in action")
mongodb_node.addBook("scaling mongodb")

def node_to_dict(node):
    def helper(n):
        if n.childCount() > 0:
            return { c.name(): helper(c) for c in n._children }
        else:
            return list(n._books)
    return { node.name(): helper(node) }

The result of node_to_dict(rootNode) does == your expected output.

Upvotes: 1

Ajax1234
Ajax1234

Reputation: 71471

Simple recursive function:

def to_dict(node):
  if isinstance(node, nodeData.Node):
     return {node._name:to_dict(node._children)}
  if all(isinstance(i, nodeData.Node) for i in node):
     return (lambda x:x if all(x.values()) else list(x))({i._name:to_dict(i._children) for i in node})
  return node

print(to_dict(rootNode))

Output:

{'books': {'web': {'front-end': {'html': ['the missing manual', 'core html5 canvas'], 'css': ['css pocket reference', 'css in depth'], 'js': ["you don't know js", 'eloquent javascript']}, 'back-end': {'php': ['modern php'], 'python': ['dive into python', 'python for everybody', 'Think Python']}}, 'database': {'sql': ['mysql', 'postgresql'], 'nosql': ['mongodb', 'cassandra']}}}

Upvotes: 0

Related Questions