# range-key-dict-2
[](https://pypi.org/project/range-key-dict-2/)
[](https://pypi.org/project/range-key-dict-2/)
[](https://github.com/odosmatthews/range-key-dict-2/actions)
[](https://codecov.io/gh/odosmatthews/range-key-dict-2)
[](https://opensource.org/licenses/MIT)
A modern, feature-rich Python dictionary that uses numeric ranges as keys. Perfect for mapping continuous ranges of numbers to values, with O(M) lookup performance and full dict-like interface.
## ๐ฏ Credit & Inspiration
This project is directly inspired by and builds upon the excellent work of **Albert Li (menglong.li)** in the original [range-key-dict](https://github.com/albertmenglongli/range-key-dict) project. `range-key-dict-2` modernizes the concept with:
- Python 3.8+ features (type hints, modern syntax)
- Full dictionary-like interface
- Overlapping range strategies
- Open-ended ranges (infinite bounds)
- Comprehensive test coverage (93 tests, 98% coverage)
- Modern tooling and CI/CD
## โจ Features
### Core Capabilities
- **Range-based Keys**: Use numeric ranges `(start, end)` as dictionary keys
- **Efficient Lookups**: Query which range contains a given number
- **Full Dict Interface**: Supports `keys()`, `values()`, `items()`, `len()`, `in`, iteration, and more
- **Mutable Operations**: Add, update, and delete ranges dynamically with `__setitem__` and `__delitem__`
- **Type Safety**: Fully typed with mypy strict mode support
### Advanced Features
- **Open-ended Ranges**: Use `None` for infinite bounds (e.g., `(None, 0)` for all negative numbers)
- **Overlap Strategies**: Control behavior when ranges overlap:
- `'error'`: Raise exception (default, backwards compatible)
- `'first'`: Return first matching range (by insertion order)
- `'last'`: Return last matching range (by insertion order)
- `'shortest'`: Return shortest matching range
- `'longest'`: Return longest matching range
- **Flexible Types**: Works with integers, floats, and mixed types
- **Backwards Compatible**: 100% compatible with original `range-key-dict` v1 API
## ๐ฆ Installation
```bash
pip install range-key-dict-2
```
## ๐ Quick Start
### Basic Usage
```python
from range_key_dict import RangeKeyDict
# Create a range dictionary
grades = RangeKeyDict({
(0, 60): 'F',
(60, 70): 'D',
(70, 80): 'C',
(80, 90): 'B',
(90, 100): 'A',
})
# Look up values
print(grades[85]) # 'B'
print(grades[92]) # 'A'
print(grades[58]) # 'F'
# Safe lookup with default
print(grades.get(105, 'Out of range')) # 'Out of range'
# Check membership
print(75 in grades) # True
print(105 in grades) # False
```
### Dict-like Interface
```python
from range_key_dict import RangeKeyDict
rkd = RangeKeyDict()
# Add ranges
rkd[(0, 10)] = 'first'
rkd[(10, 20)] = 'second'
rkd[(20, 30)] = 'third'
# Update a range
rkd[(10, 20)] = 'updated second'
# Delete a range
del rkd[(20, 30)]
# Iterate
for range_key in rkd:
print(f"Range {range_key} -> {rkd[range_key]}")
# Get all keys, values, items
print(rkd.keys()) # [(0, 10), (10, 20)]
print(rkd.values()) # ['first', 'updated second']
print(rkd.items()) # [((0, 10), 'first'), ((10, 20), 'updated second')]
# Length
print(len(rkd)) # 2
```
### Open-ended Ranges
Use `None` for infinite boundaries:
```python
from range_key_dict import RangeKeyDict
temperature = RangeKeyDict({
(None, 0): 'freezing', # (-โ, 0)
(0, 20): 'cold', # [0, 20)
(20, 30): 'comfortable', # [20, 30)
(30, None): 'hot', # [30, +โ)
})
print(temperature[-100]) # 'freezing'
print(temperature[25]) # 'comfortable'
print(temperature[50]) # 'hot'
```
### Overlapping Ranges
Control how overlapping ranges are handled:
```python
from range_key_dict import RangeKeyDict
# Allow overlaps, return first matching range
rkd = RangeKeyDict({
(0, 100): 'wide',
(25, 75): 'narrow',
}, overlap_strategy='first')
print(rkd[50]) # 'wide' (first defined range wins)
# Return shortest matching range
rkd = RangeKeyDict({
(0, 100): 'wide',
(25, 75): 'narrow',
}, overlap_strategy='shortest')
print(rkd[50]) # 'narrow' (shortest matching range)
```
Available strategies: `'error'`, `'first'`, `'last'`, `'shortest'`, `'longest'`
## ๐ Examples
Comprehensive examples available in **two formats**:
### ๐ Jupyter Notebooks (Recommended)
**[examples/](examples/)** - Interactive notebooks with pre-executed outputs
- `01_basic_usage.ipynb` - Get started with the basics (8 examples)
- `02_dict_interface.ipynb` - Master the dict-like API (10 examples)
- `03_open_ended_ranges.ipynb` - Work with infinity bounds (10 examples)
- `04_overlap_strategies.ipynb` - Handle overlapping ranges (10 examples)
- `05_real_world_use_cases.ipynb` - Production-ready examples (10 examples)
```bash
cd examples
jupyter notebook
```
### ๐ Python Scripts
**[examples_code/](examples_code/)** - Runnable Python scripts
```bash
cd examples_code
python 01_basic_usage.py
# or
bash run_all.sh
```
## ๐ Real-World Examples
### Age Categories
```python
age_groups = RangeKeyDict({
(None, 13): 'child',
(13, 20): 'teenager',
(20, 65): 'adult',
(65, None): 'senior',
})
print(age_groups[8]) # 'child'
print(age_groups[16]) # 'teenager'
print(age_groups[45]) # 'adult'
print(age_groups[70]) # 'senior'
```
### Tax Brackets
```python
tax_brackets_2024 = RangeKeyDict({
(0, 11000): 0.10,
(11000, 44725): 0.12,
(44725, 95375): 0.22,
(95375, 182100): 0.24,
(182100, 231250): 0.32,
(231250, 578125): 0.35,
(578125, None): 0.37,
})
income = 50000
tax_rate = tax_brackets_2024[income]
print(f"Tax rate for ${income}: {tax_rate:.0%}") # Tax rate for $50000: 22%
```
### HTTP Status Code Categories
```python
http_categories = RangeKeyDict({
(100, 200): 'Informational',
(200, 300): 'Success',
(300, 400): 'Redirection',
(400, 500): 'Client Error',
(500, 600): 'Server Error',
})
print(http_categories[200]) # 'Success'
print(http_categories[404]) # 'Client Error'
print(http_categories[500]) # 'Server Error'
```
## ๐ Migration from v1
`range-key-dict-2` is 100% backwards compatible with `range-key-dict` v1:
```python
# This works exactly the same in v1 and v2
from range_key_dict import RangeKeyDict
rkd = RangeKeyDict({
(0, 100): 'A',
(100, 200): 'B',
(200, 300): 'C',
})
assert rkd[70] == 'A'
assert rkd[170] == 'B'
assert rkd.get(1000, 'D') == 'D'
```
Simply change your dependency from `range-key-dict` to `range-key-dict-2` and enjoy the new features!
## โก Performance
Current implementation uses O(M) linear scan for lookups, where M is the number of ranges. Performance is excellent for small to moderate numbers of ranges (< 1000).
### Performance Roadmap
Future versions will implement O(log M) binary search optimization using:
- Sorted list of range starts with `bisect` module
- Interval trees for complex overlap scenarios
- See [TODO in source](range_key_dict/range_key_dict.py) for details
For most use cases, current performance is more than sufficient. The linear scan is simple, correct, and fast enough.
## ๐งช Testing
The package includes comprehensive tests:
```bash
# Run tests
pytest
# Run with coverage
pytest --cov=range_key_dict --cov-report=html
# Run specific test file
pytest tests/test_backwards_compatibility.py
```
Current test coverage: **98%** (93 tests)
## ๐ ๏ธ Development
### Setup Development Environment
```bash
# Clone the repository
git clone https://github.com/odosmatthews/range-key-dict-2.git
cd range-key-dict-2
# Install in development mode with dev dependencies
pip install -e ".[dev]"
# Install pre-commit hooks
pre-commit install
```
### Run Quality Checks
```bash
# Lint and auto-fix with ruff (fast!)
ruff check --fix .
# Format check with ruff
ruff format .
# Alternative: Format with black
black range_key_dict tests
# Sort imports
isort range_key_dict tests
# Type check
mypy range_key_dict
# Run all checks (pre-commit)
pre-commit run --all-files
```
## ๐ Requirements
- Python 3.8+
- No runtime dependencies!
## ๐ค Contributing
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
1. Fork the repository
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
4. Push to the branch (`git push origin feature/AmazingFeature`)
5. Open a Pull Request
## ๐ License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
## ๐ Acknowledgments
- **Albert Li (menglong.li)** - Original [range-key-dict](https://github.com/albertmenglongli/range-key-dict) creator
- All contributors who help improve this project
## ๐ Related Projects
- [range-key-dict](https://github.com/albertmenglongli/range-key-dict) - The original implementation
- [intervaltree](https://github.com/chaimleib/intervaltree) - For more complex interval operations
- [portion](https://github.com/AlexandreDecan/portion) - Advanced interval arithmetic
## ๐ฎ Contact & Support
- **Issues**: [GitHub Issues](https://github.com/odosmatthews/range-key-dict-2/issues)
- **Discussions**: [GitHub Discussions](https://github.com/odosmatthews/range-key-dict-2/discussions)
---
Made with โค๏ธ by Matthew Odos, inspired by Albert Li's original work.
Raw data
{
"_id": null,
"home_page": null,
"name": "range-key-dict-2",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": "Matthew Odos <odosmatthews@example.com>",
"keywords": "range, dict, interval, mapping, data-structure",
"author": null,
"author_email": "Matthew Odos <odosmatthews@example.com>",
"download_url": "https://files.pythonhosted.org/packages/2e/29/18a511eb39142ff97deb6711843a2fa4a037f33d78af6bdd8e64977d171a/range_key_dict_2-2.0.0.tar.gz",
"platform": null,
"description": "# range-key-dict-2\n\n[](https://pypi.org/project/range-key-dict-2/)\n[](https://pypi.org/project/range-key-dict-2/)\n[](https://github.com/odosmatthews/range-key-dict-2/actions)\n[](https://codecov.io/gh/odosmatthews/range-key-dict-2)\n[](https://opensource.org/licenses/MIT)\n\nA modern, feature-rich Python dictionary that uses numeric ranges as keys. Perfect for mapping continuous ranges of numbers to values, with O(M) lookup performance and full dict-like interface.\n\n## \ud83c\udfaf Credit & Inspiration\n\nThis project is directly inspired by and builds upon the excellent work of **Albert Li (menglong.li)** in the original [range-key-dict](https://github.com/albertmenglongli/range-key-dict) project. `range-key-dict-2` modernizes the concept with:\n\n- Python 3.8+ features (type hints, modern syntax)\n- Full dictionary-like interface\n- Overlapping range strategies\n- Open-ended ranges (infinite bounds)\n- Comprehensive test coverage (93 tests, 98% coverage)\n- Modern tooling and CI/CD\n\n## \u2728 Features\n\n### Core Capabilities\n\n- **Range-based Keys**: Use numeric ranges `(start, end)` as dictionary keys\n- **Efficient Lookups**: Query which range contains a given number\n- **Full Dict Interface**: Supports `keys()`, `values()`, `items()`, `len()`, `in`, iteration, and more\n- **Mutable Operations**: Add, update, and delete ranges dynamically with `__setitem__` and `__delitem__`\n- **Type Safety**: Fully typed with mypy strict mode support\n\n### Advanced Features\n\n- **Open-ended Ranges**: Use `None` for infinite bounds (e.g., `(None, 0)` for all negative numbers)\n- **Overlap Strategies**: Control behavior when ranges overlap:\n - `'error'`: Raise exception (default, backwards compatible)\n - `'first'`: Return first matching range (by insertion order)\n - `'last'`: Return last matching range (by insertion order)\n - `'shortest'`: Return shortest matching range\n - `'longest'`: Return longest matching range\n- **Flexible Types**: Works with integers, floats, and mixed types\n- **Backwards Compatible**: 100% compatible with original `range-key-dict` v1 API\n\n## \ud83d\udce6 Installation\n\n```bash\npip install range-key-dict-2\n```\n\n## \ud83d\ude80 Quick Start\n\n### Basic Usage\n\n```python\nfrom range_key_dict import RangeKeyDict\n\n# Create a range dictionary\ngrades = RangeKeyDict({\n (0, 60): 'F',\n (60, 70): 'D',\n (70, 80): 'C',\n (80, 90): 'B',\n (90, 100): 'A',\n})\n\n# Look up values\nprint(grades[85]) # 'B'\nprint(grades[92]) # 'A'\nprint(grades[58]) # 'F'\n\n# Safe lookup with default\nprint(grades.get(105, 'Out of range')) # 'Out of range'\n\n# Check membership\nprint(75 in grades) # True\nprint(105 in grades) # False\n```\n\n### Dict-like Interface\n\n```python\nfrom range_key_dict import RangeKeyDict\n\nrkd = RangeKeyDict()\n\n# Add ranges\nrkd[(0, 10)] = 'first'\nrkd[(10, 20)] = 'second'\nrkd[(20, 30)] = 'third'\n\n# Update a range\nrkd[(10, 20)] = 'updated second'\n\n# Delete a range\ndel rkd[(20, 30)]\n\n# Iterate\nfor range_key in rkd:\n print(f\"Range {range_key} -> {rkd[range_key]}\")\n\n# Get all keys, values, items\nprint(rkd.keys()) # [(0, 10), (10, 20)]\nprint(rkd.values()) # ['first', 'updated second']\nprint(rkd.items()) # [((0, 10), 'first'), ((10, 20), 'updated second')]\n\n# Length\nprint(len(rkd)) # 2\n```\n\n### Open-ended Ranges\n\nUse `None` for infinite boundaries:\n\n```python\nfrom range_key_dict import RangeKeyDict\n\ntemperature = RangeKeyDict({\n (None, 0): 'freezing', # (-\u221e, 0)\n (0, 20): 'cold', # [0, 20)\n (20, 30): 'comfortable', # [20, 30)\n (30, None): 'hot', # [30, +\u221e)\n})\n\nprint(temperature[-100]) # 'freezing'\nprint(temperature[25]) # 'comfortable'\nprint(temperature[50]) # 'hot'\n```\n\n### Overlapping Ranges\n\nControl how overlapping ranges are handled:\n\n```python\nfrom range_key_dict import RangeKeyDict\n\n# Allow overlaps, return first matching range\nrkd = RangeKeyDict({\n (0, 100): 'wide',\n (25, 75): 'narrow',\n}, overlap_strategy='first')\n\nprint(rkd[50]) # 'wide' (first defined range wins)\n\n# Return shortest matching range\nrkd = RangeKeyDict({\n (0, 100): 'wide',\n (25, 75): 'narrow',\n}, overlap_strategy='shortest')\n\nprint(rkd[50]) # 'narrow' (shortest matching range)\n```\n\nAvailable strategies: `'error'`, `'first'`, `'last'`, `'shortest'`, `'longest'`\n\n## \ud83d\udcd6 Examples\n\nComprehensive examples available in **two formats**:\n\n### \ud83d\udcd3 Jupyter Notebooks (Recommended)\n**[examples/](examples/)** - Interactive notebooks with pre-executed outputs\n\n- `01_basic_usage.ipynb` - Get started with the basics (8 examples)\n- `02_dict_interface.ipynb` - Master the dict-like API (10 examples)\n- `03_open_ended_ranges.ipynb` - Work with infinity bounds (10 examples)\n- `04_overlap_strategies.ipynb` - Handle overlapping ranges (10 examples)\n- `05_real_world_use_cases.ipynb` - Production-ready examples (10 examples)\n\n```bash\ncd examples\njupyter notebook\n```\n\n### \ud83d\udc0d Python Scripts\n**[examples_code/](examples_code/)** - Runnable Python scripts\n\n```bash\ncd examples_code\npython 01_basic_usage.py\n# or\nbash run_all.sh\n```\n\n## \ud83d\udcd6 Real-World Examples\n\n### Age Categories\n\n```python\nage_groups = RangeKeyDict({\n (None, 13): 'child',\n (13, 20): 'teenager',\n (20, 65): 'adult',\n (65, None): 'senior',\n})\n\nprint(age_groups[8]) # 'child'\nprint(age_groups[16]) # 'teenager'\nprint(age_groups[45]) # 'adult'\nprint(age_groups[70]) # 'senior'\n```\n\n### Tax Brackets\n\n```python\ntax_brackets_2024 = RangeKeyDict({\n (0, 11000): 0.10,\n (11000, 44725): 0.12,\n (44725, 95375): 0.22,\n (95375, 182100): 0.24,\n (182100, 231250): 0.32,\n (231250, 578125): 0.35,\n (578125, None): 0.37,\n})\n\nincome = 50000\ntax_rate = tax_brackets_2024[income]\nprint(f\"Tax rate for ${income}: {tax_rate:.0%}\") # Tax rate for $50000: 22%\n```\n\n### HTTP Status Code Categories\n\n```python\nhttp_categories = RangeKeyDict({\n (100, 200): 'Informational',\n (200, 300): 'Success',\n (300, 400): 'Redirection',\n (400, 500): 'Client Error',\n (500, 600): 'Server Error',\n})\n\nprint(http_categories[200]) # 'Success'\nprint(http_categories[404]) # 'Client Error'\nprint(http_categories[500]) # 'Server Error'\n```\n\n## \ud83d\udd04 Migration from v1\n\n`range-key-dict-2` is 100% backwards compatible with `range-key-dict` v1:\n\n```python\n# This works exactly the same in v1 and v2\nfrom range_key_dict import RangeKeyDict\n\nrkd = RangeKeyDict({\n (0, 100): 'A',\n (100, 200): 'B',\n (200, 300): 'C',\n})\n\nassert rkd[70] == 'A'\nassert rkd[170] == 'B'\nassert rkd.get(1000, 'D') == 'D'\n```\n\nSimply change your dependency from `range-key-dict` to `range-key-dict-2` and enjoy the new features!\n\n## \u26a1 Performance\n\nCurrent implementation uses O(M) linear scan for lookups, where M is the number of ranges. Performance is excellent for small to moderate numbers of ranges (< 1000).\n\n### Performance Roadmap\n\nFuture versions will implement O(log M) binary search optimization using:\n- Sorted list of range starts with `bisect` module\n- Interval trees for complex overlap scenarios\n- See [TODO in source](range_key_dict/range_key_dict.py) for details\n\nFor most use cases, current performance is more than sufficient. The linear scan is simple, correct, and fast enough.\n\n## \ud83e\uddea Testing\n\nThe package includes comprehensive tests:\n\n```bash\n# Run tests\npytest\n\n# Run with coverage\npytest --cov=range_key_dict --cov-report=html\n\n# Run specific test file\npytest tests/test_backwards_compatibility.py\n```\n\nCurrent test coverage: **98%** (93 tests)\n\n## \ud83d\udee0\ufe0f Development\n\n### Setup Development Environment\n\n```bash\n# Clone the repository\ngit clone https://github.com/odosmatthews/range-key-dict-2.git\ncd range-key-dict-2\n\n# Install in development mode with dev dependencies\npip install -e \".[dev]\"\n\n# Install pre-commit hooks\npre-commit install\n```\n\n### Run Quality Checks\n\n```bash\n# Lint and auto-fix with ruff (fast!)\nruff check --fix .\n\n# Format check with ruff\nruff format .\n\n# Alternative: Format with black\nblack range_key_dict tests\n\n# Sort imports\nisort range_key_dict tests\n\n# Type check\nmypy range_key_dict\n\n# Run all checks (pre-commit)\npre-commit run --all-files\n```\n\n## \ud83d\udccb Requirements\n\n- Python 3.8+\n- No runtime dependencies!\n\n## \ud83e\udd1d Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.\n\n1. Fork the repository\n2. Create your feature branch (`git checkout -b feature/AmazingFeature`)\n3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)\n4. Push to the branch (`git push origin feature/AmazingFeature`)\n5. Open a Pull Request\n\n## \ud83d\udcc4 License\n\nThis project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.\n\n## \ud83d\ude4f Acknowledgments\n\n- **Albert Li (menglong.li)** - Original [range-key-dict](https://github.com/albertmenglongli/range-key-dict) creator\n- All contributors who help improve this project\n\n## \ud83d\udcda Related Projects\n\n- [range-key-dict](https://github.com/albertmenglongli/range-key-dict) - The original implementation\n- [intervaltree](https://github.com/chaimleib/intervaltree) - For more complex interval operations\n- [portion](https://github.com/AlexandreDecan/portion) - Advanced interval arithmetic\n\n## \ud83d\udcee Contact & Support\n\n- **Issues**: [GitHub Issues](https://github.com/odosmatthews/range-key-dict-2/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/odosmatthews/range-key-dict-2/discussions)\n\n---\n\nMade with \u2764\ufe0f by Matthew Odos, inspired by Albert Li's original work.\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "A modern dict implementation supporting range-based keys with O(log M) lookups",
"version": "2.0.0",
"project_urls": {
"Bug Tracker": "https://github.com/odosmatthews/range-key-dict-2/issues",
"Homepage": "https://github.com/odosmatthews/range-key-dict-2",
"Original Project": "https://github.com/albertmenglongli/range-key-dict",
"Source Code": "https://github.com/odosmatthews/range-key-dict-2"
},
"split_keywords": [
"range",
" dict",
" interval",
" mapping",
" data-structure"
],
"urls": [
{
"comment_text": null,
"digests": {
"blake2b_256": "0f49c10ee0826d692ea04ee28bf3f6dda8e354bb5c261b061acab81f12ff271c",
"md5": "14ee735680990196ae3df181c9ba81ab",
"sha256": "b460682107f6eb2810ff83bd33547971ed36924f1d506d1c981808a3cbd28cd6"
},
"downloads": -1,
"filename": "range_key_dict_2-2.0.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "14ee735680990196ae3df181c9ba81ab",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.8",
"size": 10410,
"upload_time": "2025-10-21T22:34:08",
"upload_time_iso_8601": "2025-10-21T22:34:08.057497Z",
"url": "https://files.pythonhosted.org/packages/0f/49/c10ee0826d692ea04ee28bf3f6dda8e354bb5c261b061acab81f12ff271c/range_key_dict_2-2.0.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": null,
"digests": {
"blake2b_256": "2e2918a511eb39142ff97deb6711843a2fa4a037f33d78af6bdd8e64977d171a",
"md5": "e14184a315252346a6ac910a7db89021",
"sha256": "3348515fda514c0f718e94231344af6ddb1f8f16f93ffcb3683120d116b9dea9"
},
"downloads": -1,
"filename": "range_key_dict_2-2.0.0.tar.gz",
"has_sig": false,
"md5_digest": "e14184a315252346a6ac910a7db89021",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 20501,
"upload_time": "2025-10-21T22:34:09",
"upload_time_iso_8601": "2025-10-21T22:34:09.236682Z",
"url": "https://files.pythonhosted.org/packages/2e/29/18a511eb39142ff97deb6711843a2fa4a037f33d78af6bdd8e64977d171a/range_key_dict_2-2.0.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2025-10-21 22:34:09",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "odosmatthews",
"github_project": "range-key-dict-2",
"github_not_found": true,
"lcname": "range-key-dict-2"
}