CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-collections-extended

Extra Python Collections - bags (multisets) and setlists (ordered sets)

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

range-maps.mddocs/

Range Mappings

RangeMaps provide efficient mappings from continuous ranges to values using interval-based storage. They support slice notation, range operations, and are ideal for time series data, version ranges, and any scenario requiring interval-to-value mappings.

Capabilities

RangeMap Construction

Create range mappings from various input formats with optional default values.

class RangeMap:
    def __init__(self, iterable=None, default_value=NOT_SET):
        """Create a RangeMap.
        
        Args:
            iterable: Mapping (start -> value) or iterable of (start, stop, value) tuples
            default_value: Default value for unmapped ranges
        """

@classmethod
def from_mapping(cls, mapping):
    """Create RangeMap from mapping of range starts to values.
    
    Args:
        mapping: Dict mapping start points to values
        
    Returns:
        RangeMap: New range map
    """

@classmethod
def from_iterable(cls, iterable):
    """Create RangeMap from iterable of (start, stop, value) tuples.
    
    Args:
        iterable: Iterable of (start, stop, value) tuples
        
    Returns:
        RangeMap: New range map
    """

Usage examples:

from collections_extended import RangeMap
from datetime import date

# Empty range map
rm = RangeMap()

# With default value for unmapped ranges
rm_default = RangeMap(default_value='unknown')

# From mapping of start points
starts = {0: 'low', 10: 'medium', 20: 'high'}
rm_starts = RangeMap.from_mapping(starts)

# From tuples of (start, stop, value)
ranges = [(0, 5, 'first'), (5, 10, 'second'), (10, 15, 'third')]
rm_ranges = RangeMap.from_iterable(ranges)

Range Setting and Access

Set and retrieve values for specific ranges using slice notation or method calls.

def set(self, value, start=None, stop=None):
    """Set range from start to stop to value.
    
    Args:
        value: Value to assign to the range
        start: Start of range (inclusive, None for unbounded left)
        stop: End of range (exclusive, None for unbounded right)
    """

def get(self, key, restval=None):
    """Get value for a specific key.
    
    Args:
        key: Point to look up
        restval: Value to return if key not mapped
        
    Returns:
        Any: Value at key or restval if unmapped
    """

def __getitem__(self, key):
    """Get value for key or range slice.
    
    Args:
        key: Point to look up or slice for sub-range
        
    Returns:
        Any: Value at key or RangeMap for slice
        
    Raises:
        KeyError: If key not mapped and no default value
    """

def __setitem__(self, key, value):
    """Set range using slice notation.
    
    Args:
        key: slice object defining the range
        value: Value to assign to the range
    """

Usage examples:

rm = RangeMap()

# Set ranges using method calls
rm.set('morning', start=6, stop=12)
rm.set('afternoon', start=12, stop=18)
rm.set('evening', start=18, stop=22)

# Set ranges using slice notation  
rm[0:6] = 'night'
rm[22:24] = 'late_night'

# Access specific points
print(rm[8])    # 'morning'
print(rm[15])   # 'afternoon'
print(rm.get(25, 'unmapped'))  # 'unmapped'

# Access sub-ranges
sub_range = rm[10:20]  # RangeMap with overlapping ranges

Range Queries and Iteration

Query range information and iterate over mapped intervals.

def ranges(self, start=None, stop=None):
    """Generate MappedRange objects for ranges in the specified interval.
    
    Args:
        start: Start of query interval (None for unbounded)
        stop: End of query interval (None for unbounded)
        
    Yields:
        MappedRange: Range objects with start, stop, and value
    """

def get_range(self, start=None, stop=None):
    """Return a RangeMap covering the specified range.
    
    Args:
        start: Start of range to extract
        stop: End of range to extract
        
    Returns:
        RangeMap: New RangeMap covering the specified range
    """

def __contains__(self, key):
    """Check if key is mapped to a value.
    
    Args:
        key: Point to check
        
    Returns:
        bool: True if key is mapped
    """

def __iter__(self):
    """Iterate over range start points that have mappings."""

def __len__(self):
    """Return number of mapped ranges.
    
    Returns:
        int: Count of ranges with values
    """

def __bool__(self):
    """Return True if any ranges are mapped.
    
    Returns:
        bool: True if RangeMap contains mappings
    """

Usage examples:

rm = RangeMap()
rm[0:10] = 'first'
rm[10:20] = 'second' 
rm[30:40] = 'third'

# Iterate over all ranges
for range_obj in rm.ranges():
    print(f"[{range_obj.start}:{range_obj.stop}) -> {range_obj.value}")

# Query specific interval
for range_obj in rm.ranges(5, 35):
    print(f"Overlaps query: [{range_obj.start}:{range_obj.stop}) -> {range_obj.value}")

# Extract sub-range
partial = rm.get_range(5, 25)  # Includes parts of 'first' and 'second'

# Basic queries
print(5 in rm)      # True
print(25 in rm)     # False  
print(len(rm))      # 3
print(bool(rm))     # True

Range Modification

Modify, delete, and clear ranges efficiently.

def delete(self, start=None, stop=None):
    """Delete the specified range.
    
    Args:
        start: Start of range to delete (None for unbounded)
        stop: End of range to delete (None for unbounded)
        
    Raises:
        KeyError: If any part of the range is not mapped
    """

def empty(self, start=None, stop=None):
    """Empty the specified range (like delete but doesn't raise errors).
    
    Args:
        start: Start of range to empty (None for unbounded)
        stop: End of range to empty (None for unbounded)
    """

def clear(self):
    """Remove all mappings from the RangeMap."""

def __delitem__(self, key):
    """Delete range using slice notation.
    
    Args:
        key: slice object defining range to delete
    """

Usage examples:

rm = RangeMap()
rm[0:50] = 'full_range'
rm[10:20] = 'middle'
rm[30:40] = 'end'

# Delete specific range
del rm[15:25]  # Removes part of 'middle' and gap

# Empty range (no error if unmapped)
rm.empty(45, 60)  # Removes part of range, no error for unmapped part

# Delete with error checking
try:
    rm.delete(100, 110)  # KeyError - range not mapped
except KeyError:
    print("Range not fully mapped")

# Clear everything
rm.clear()
print(len(rm))  # 0

Boundary Properties

Access information about range boundaries and extents.

@property
def start(self):
    """Get the start of the first mapped range.
    
    Returns:
        Any: Start point of first range or None if empty/unbounded
    """

@property
def end(self):
    """Get the end of the last mapped range.
    
    Returns:
        Any: End point of last range or None if empty/unbounded
    """

Usage examples:

rm = RangeMap()
rm[10:20] = 'first'
rm[30:40] = 'second'

print(rm.start)  # 10
print(rm.end)    # 40

# Unbounded ranges
rm[:5] = 'prefix'      # Unbounded left
rm[50:] = 'suffix'     # Unbounded right

print(rm.start)  # None (unbounded left)
print(rm.end)    # None (unbounded right)

MappedRange Objects

Represent individual range mappings with start, stop, and value information.

class MappedRange:
    def __init__(self, start, stop, value):
        """Create a mapped range.
        
        Args:
            start: Range start (inclusive)
            stop: Range end (exclusive)  
            value: Associated value
        """
    
    start: Any      # Range start point (inclusive)
    stop: Any       # Range end point (exclusive)
    value: Any      # Associated value
    
    def __iter__(self):
        """Allow unpacking: start, stop, value = mapped_range"""
    
    def __eq__(self, other):
        """Check equality with another MappedRange."""
    
    def __str__(self):
        """String representation: [start, stop) -> value"""
    
    def __repr__(self):
        """Detailed representation for debugging."""

Usage examples:

from collections_extended import MappedRange

# Create manually
mr = MappedRange(0, 10, 'first_range')
print(mr)  # [0, 10) -> first_range

# Unpack from ranges() iteration
rm = RangeMap()
rm[0:10] = 'test'
for start, stop, value in rm.ranges():
    print(f"Range from {start} to {stop}: {value}")

# Access attributes
for range_obj in rm.ranges():
    print(f"Start: {range_obj.start}")
    print(f"Stop: {range_obj.stop}")  
    print(f"Value: {range_obj.value}")

View Operations

Access keys, values, and items through view objects.

def keys(self):
    """Return view of range start keys.
    
    Returns:
        RangeMapKeysView: View iterating over range start points
    """

def values(self):
    """Return view of range values.
    
    Returns:
        RangeMapValuesView: View iterating over unique values
    """

def items(self):
    """Return view of (start, value) pairs.
    
    Returns:
        RangeMapItemsView: View iterating over start-value pairs
    """

Usage examples:

rm = RangeMap()
rm[0:10] = 'first'
rm[10:20] = 'second'
rm[20:30] = 'first'  # Same value as first range

print(list(rm.keys()))    # [0, 10, 20]
print(list(rm.values()))  # ['first', 'second'] - unique values only
print(list(rm.items()))   # [(0, 'first'), (10, 'second'), (20, 'first')]

# Views support membership testing
print(15 in rm.keys())    # False (15 is not a start point)
print('first' in rm.values())  # True
print((10, 'second') in rm.items())  # True

Advanced Usage Patterns

Common patterns for effective RangeMap usage.

from datetime import date, timedelta

# Version ranges for software releases
versions = RangeMap()
versions[date(2020, 1, 1):date(2020, 6, 1)] = '1.0'
versions[date(2020, 6, 1):date(2021, 1, 1)] = '1.1' 
versions[date(2021, 1, 1):] = '2.0'

current_version = versions[date.today()]

# Price tiers based on quantity
pricing = RangeMap(default_value=0.50)  # Default unit price
pricing[100:500] = 0.45    # Volume discount
pricing[500:1000] = 0.40   # Larger volume discount  
pricing[1000:] = 0.35      # Highest volume discount

unit_price = pricing[quantity]

# Time-based scheduling
schedule = RangeMap()
schedule[6:9] = 'morning_shift'
schedule[9:17] = 'day_shift'
schedule[17:22] = 'evening_shift'
schedule[22:6] = 'night_shift'  # Wraps around (if using 24-hour periods)

current_shift = schedule.get(current_hour, 'off_duty')

# Overlapping range updates
temp_map = RangeMap()
temp_map[0:100] = 'baseline'
temp_map[20:30] = 'override'  # Splits baseline into [0:20) and [30:100)

# Merging adjacent ranges with same value
data = RangeMap()
data[0:10] = 'same'
data[10:20] = 'same'  # Automatically merged into [0:20) = 'same'

Install with Tessl CLI

npx tessl i tessl/pypi-collections-extended

docs

bags.md

bijections.md

index.md

indexed-dicts.md

range-maps.md

setlists.md

tile.json