Skip to main content

FlexTree - A flexible and intuitive Python library for creating and manipulating tree data structures

Project description

FlexTree

A flexible and intuitive Python library for creating and manipulating tree data structures.

Warning Some code or documentation in this project may be generated by AI. While the developer has tested the code, please review and validate all outputs before using them in production. Use at your own risk.

Features

  • TreeNode: Individual nodes with name, content, and parent-child relationships
  • Tree: Complete tree structure with operations for insertion, deletion, and modification
  • JSON Serialization: Save and load trees to/from JSON files
  • Tree Visualization: ASCII art tree drawing functionality
  • Flexible Content: Store any Python object as node content
  • Search Operations: Find nodes by name or index
  • Tree Statistics: Calculate depth, width, and node count metrics

Installation

Install using pip:

pip install flextree

Or, you can install by cloning this repository:

git clone https://github.com/znzhao/flextree.git
cd flextree
pip install -e .

Quick Start

Creating a Flex Tree

from flextree import TreeNode, Tree, draw_tree

# Create nodes
root = TreeNode("Company", "Acme Corp")
engineering = TreeNode("Engineering", "Tech Department")
marketing = TreeNode("Marketing", "Marketing Department")
backend = TreeNode("Backend", "Server Team")
frontend = TreeNode("Frontend", "UI Team")

# Build tree structure
root.add_child(engineering)
root.add_child(marketing)
engineering.add_child(backend)
engineering.add_child(frontend)

# Create a tree object
company_tree = Tree(root)

# Visualize the tree
draw_tree(root)

Output:

└── Company: Acme Corp
    ├── Engineering: Tech Department
    │   ├── Backend: Server Team
    │   └── Frontend: UI Team
    └── Marketing: Marketing Department

Working with Tree Operations

# Insert a new department
new_dept = TreeNode("HR", "Human Resources")
company_tree.insert("Company", new_dept)

# Modify existing content
company_tree.alter("Backend", "Backend Development Team")

# Get a subtree
eng_subtree = company_tree.get("Engineering")
print(eng_subtree.summary())

# Delete a node
company_tree.delete("HR")

Pythonic Tree Navigation with getitem

Flex Tree supports intuitive Python-style indexing for tree navigation using square bracket notation. This makes working with trees feel natural and pythonic.

# Tree objects support getitem operations
company_tree = Tree(root)

# Access child by name - returns Tree object
eng_dept = company_tree["Engineering"]
print(f"Department: {eng_dept.root.name}")

# Multiple selection using string lists
departments = company_tree[["Engineering", "Marketing"]]
print(f"Selected: {[tree.root.name for tree in departments]}")

# Chain operations for deep navigation
if eng_dept:
    # Further navigation on the subtree
    print(f"Engineering has {len(eng_dept.root.children)} teams")

Important Notes:

  • Only Tree objects support getitem operations (tree[key])
  • TreeNode objects do not support getitem - use node.children for direct access
  • Tree getitem returns Tree objects (single) or List[Tree] (multiple)
  • Multiple selection only supports string-only lists: tree[["name1", "name2"]]
  • For other access patterns (by index, slicing), work with TreeNode.children directly

Storing Complex Data

# Nodes can store any Python object
employee_data = {
    "name": "John Doe",
    "role": "Senior Developer",
    "skills": ["Python", "JavaScript", "Docker"],
    "start_date": "2023-01-15"
}

employee_node = TreeNode("john_doe", employee_data)
backend.add_child(employee_node)

JSON Serialization

# Save tree to JSON file
company_tree.save_json("company_structure.json")

# Load tree from JSON file
loaded_tree = Tree.load_json("company_structure.json")

Tree Statistics

# Get tree dimensions
print(f"Max depth: {company_tree.max_depth()}")
print(f"Max width: {company_tree.max_width()}")
print(f"Node count: {company_tree.count()}")
# Get node summary
print(company_tree.summary())

Tree Visualization with draw_tree

The draw_tree function provides a beautiful ASCII art representation of your tree structure using Unicode box-drawing characters. This is perfect for debugging, documentation, or simply understanding your tree's structure.

Basic Usage

from flextree import TreeNode, draw_tree

# Create a Flex tree
root = TreeNode("root", "I am root")
child1 = TreeNode("child1", "First child")
child2 = TreeNode("child2", "Second child")
grandchild = TreeNode("grandchild", "I'm nested!")

root.add_child(child1)
root.add_child(child2)
child1.add_child(grandchild)

# Draw the tree
draw_tree(root)

Output:

└── root: I am root
    ├── child1: First child
    │   └── grandchild: I'm nested!
    └── child2: Second child

Drawing Subtrees

You can draw any subtree by passing any node as the starting point:

# Draw only the subtree starting from child1
draw_tree(child1)

Output:

└── child1: First child
    └── grandchild: I'm nested!

Special Content Handling

The draw_tree function has special handling for dictionary content. By default, if your node's content is a dictionary containing a 'definition' key, it will display that value instead of the entire dictionary:

# Create nodes with dictionary content
root = TreeNode("concept", {"definition": "A tree data structure", "type": "data_structure"})
child = TreeNode("node", {"definition": "A single element in the tree", "properties": ["name", "content"]})

root.add_child(child)
draw_tree(root)

Output:

└── concept: A tree data structure
    └── node: A single element in the tree

Custom Dictionary Key

You can specify a custom key to display from dictionary content using the key parameter:

# Create nodes with custom dictionary keys
root = TreeNode("product", {"name": "Flex Tree Library", "description": "Tree data structure", "version": "1.0"})
child = TreeNode("feature", {"name": "JSON Export", "type": "functionality"})

root.add_child(child)

# Use 'name' key instead of 'definition'
draw_tree(root, key="name")

Output:

└── product: Flex Tree Library
    └── feature: JSON Export

Without a matching key, the entire dictionary would be displayed:

root = TreeNode("data", {"key": "value", "number": 42})
draw_tree(root, key="description")  # Key doesn't exist

Output:

└── data: {'key': 'value', 'number': 42}

Complex Tree Visualization

Here's a more complex example showing different content types:

# Create a knowledge tree
knowledge = TreeNode("Programming", "Software Development")

# Programming languages
languages = TreeNode("Languages", {"definition": "Programming languages", "count": 3})
python = TreeNode("Python", {"definition": "High-level programming language", "year": 1991})
javascript = TreeNode("JavaScript", {"definition": "Web programming language", "year": 1995})
java = TreeNode("Java", {"definition": "Object-oriented programming language", "year": 1995})

# Concepts
concepts = TreeNode("Concepts", "Core programming concepts")
oop = TreeNode("OOP", {"definition": "Object-Oriented Programming", "paradigm": "object-oriented"})
functional = TreeNode("Functional", {"definition": "Functional Programming", "paradigm": "functional"})

# Build the tree
knowledge.add_child(languages)
knowledge.add_child(concepts)
languages.add_child(python)
languages.add_child(javascript)
languages.add_child(java)
concepts.add_child(oop)
concepts.add_child(functional)

# Visualize
draw_tree(knowledge)

Output:

└── Programming: Software Development
    ├── Languages: Programming languages
    │   ├── Python: High-level programming language
    │   ├── JavaScript: Web programming language
    │   └── Java: Object-oriented programming language
    └── Concepts: Core programming concepts
        ├── OOP: Object-Oriented Programming
        └── Functional: Functional Programming

Using with Tree Objects

You can also use draw_tree with Tree objects by accessing their root:

tree = Tree(root)
draw_tree(tree.root)

# Or use the convenient .draw() method
tree.draw()

# With custom key
tree.draw(key="name")

Convenient .draw() Methods

Both TreeNode and Tree objects have a convenient .draw() method:

# Draw from any TreeNode
node.draw()
node.draw(key="custom_key")

# Draw entire Tree
tree.draw()
tree.draw(key="custom_key")

Tips for Better Visualization

  1. Keep node names concise: Long names can make the tree hard to read
  2. Use meaningful content: The content appears after the colon, so make it descriptive
  3. Use the 'definition' key: For dictionary content, use a 'definition' key for cleaner output
  4. Consider tree depth: Very deep trees might be hard to read in console output

Unicode Characters Used

The draw_tree function uses these Unicode box-drawing characters:

  • └── (U+2514 U+2500 U+2500): For the last child at each level
  • ├── (U+251C U+2500 U+2500): For intermediate children
  • (U+2502): For vertical continuation lines
  • (4 spaces): For padding after the last child

API Reference

TreeNode

Constructor

  • TreeNode(name: str, content: Any = None): Create a new tree node

Methods

  • add_child(child: TreeNode): Add a child node
  • remove_child(child: Union[TreeNode, str, int]): Remove a child by node, name, or index
  • get_child(key: Union[str, int]): Get child by name or index
  • set_content(content: Any): Update node content
  • get_subtree(name: str): Find and return subtree by node name
  • to_dict(): Convert node and its subtree to dictionary
  • from_dict(data: Dict): Create node from dictionary (static method)
  • max_depth(): Calculate maximum depth from this node
  • max_width(): Calculate maximum width from this node
  • summary(): Get a formatted summary of the node
  • draw(key: str = "definition"): Print ASCII art representation of the node and its subtree

Tree

Constructor

  • Tree(root: TreeNode): Create a tree with the given root node

Methods

  • insert(parent_name: str, node: TreeNode): Insert node under specified parent
  • delete(node_name: str): Delete node by name
  • alter(node_name: str, new_content: Any): Change content of specified node
  • get(key: Union[str, int]): Get subtree by name or index
  • save_json(filepath: str): Save tree to JSON file
  • load_json(filepath: str): Load tree from JSON file (static method)
  • max_depth(): Get maximum depth of entire tree
  • max_width(): Get maximum width of entire tree
  • summary(): Get formatted summary of the tree
  • draw(key: str = "definition"): Print ASCII art representation of the entire tree

Utility Functions

draw_tree

  • draw_tree(node: TreeNode, prefix: str = "", is_last: bool = True, key: str = "definition"): Print ASCII art representation of tree

Parameters:

  • node (TreeNode): The root node of the tree/subtree to visualize
  • prefix (str, optional): Internal parameter for indentation. Leave as default.
  • is_last (bool, optional): Internal parameter for formatting. Leave as default.
  • key (str, optional): Dictionary key to display for dict content. Defaults to "definition".

Special Features:

  • Uses Unicode box-drawing characters for clean tree visualization
  • Customizable handling for dictionary content with specified key
  • Recursively displays entire subtree structure
  • Works with any TreeNode as starting point

Usage Examples:

# Basic usage - draw entire tree
draw_tree(root_node)

# Draw subtree starting from any node
draw_tree(some_child_node)

# Use custom dictionary key
draw_tree(root_node, key="name")

# The function automatically handles the formatting

Examples

The Flex Tree package includes ready-to-run example files that demonstrate all the key features:

Running the Examples

from flextree import examples
examples()

Requirements

  • Python 3.6+
  • No external dependencies for core functionality

Contributing

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

License

This project is licensed under the MIT License - see the LICENSE file for details.

Changelog

Version 0.1.6

  • Maintained backward compatibility with all existing features
  • Prepared for future enhancements and optimizations
  • Consolidated changelog documentation

Version 0.1.5

  • Added pythonic getitem functionality for Tree objects
  • Enhanced examples with copy-paste ready CODE/OUTPUT format
  • Improved documentation with comprehensive getitem usage guide
  • Updated TreeNode behavior: removed getitem support (use Tree objects instead)
  • Added comprehensive test coverage for count and getitem operations
  • Refined Tree getitem to support string-only lists for multiple selection
  • Updated package documentation and README with getitem introduction

Version 0.1.4

  • Added count() methods to TreeNode and Tree classes
  • Enhanced tree statistics functionality
  • Updated examples and documentation

Version 0.1.3

  • Package name changed to "Flex Tree"
  • Improved package structure and imports
  • Added quick_examples and examples functions to public API

Version 0.1.2

  • Bug fixes and improvements
  • Enhanced tree manipulation methods

Version 0.1.1

  • Minor updates and documentation improvements

Version 0.1.0

  • Initial release
  • Basic tree node and tree operations
  • JSON serialization support
  • Tree visualization
  • Comprehensive test suite

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

flextree-0.1.7.tar.gz (23.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

flextree-0.1.7-py3-none-any.whl (19.8 kB view details)

Uploaded Python 3

File details

Details for the file flextree-0.1.7.tar.gz.

File metadata

  • Download URL: flextree-0.1.7.tar.gz
  • Upload date:
  • Size: 23.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for flextree-0.1.7.tar.gz
Algorithm Hash digest
SHA256 0d7b837ca28ba14a93350af59085ccc25ce8741925bc832a250c6e219b4a6403
MD5 b99c20c05fecc7b8979e82052ccf3e57
BLAKE2b-256 45c683c4503250c40af4bef826ac42fe981d1dbfad7b76ecfb7a3e014b85bf30

See more details on using hashes here.

File details

Details for the file flextree-0.1.7-py3-none-any.whl.

File metadata

  • Download URL: flextree-0.1.7-py3-none-any.whl
  • Upload date:
  • Size: 19.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.11

File hashes

Hashes for flextree-0.1.7-py3-none-any.whl
Algorithm Hash digest
SHA256 4c0729c25a20f7b2308cc235531c76b617a2162ce39d322f71c3037102258b2a
MD5 2e2d1f8e8a0e77edfb7e1cf7e9f87f36
BLAKE2b-256 3f3fe884e20478805e54a726acf7f5f6bd8b9e348bc542622f16e8c600879fe2

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page