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

indexed-dicts.mddocs/

Indexed Dictionaries

IndexedDict provides ordered mappings with both key-based and index-based access. It maintains insertion order while providing efficient positional access, making it ideal for scenarios requiring both dictionary semantics and list-like indexing.

Capabilities

IndexedDict Construction

Create ordered dictionaries from various input formats with both key and index access.

class IndexedDict:
    def __init__(self, iterable=None, **kwargs):
        """Create an IndexedDict.
        
        Args:
            iterable: Mapping or iterable of (key, value) pairs
            **kwargs: Additional key-value pairs
        """

Usage examples:

from collections_extended import IndexedDict

# Create empty
idict = IndexedDict()

# Create from dictionary
idict = IndexedDict({'a': 1, 'b': 2, 'c': 3})

# Create from iterable of pairs
idict = IndexedDict([('x', 10), ('y', 20), ('z', 30)])

# Create with keyword arguments
idict = IndexedDict(name='Alice', age=30, city='NYC')

# Mix approaches
idict = IndexedDict({'a': 1}, b=2, c=3)

Dual Access Patterns

Access elements by either key or index position efficiently.

def get(self, key=NOT_SET, index=NOT_SET, default=NOT_SET):
    """Get value by key or index.
    
    Args:
        key: Key to look up (mutually exclusive with index)
        index: Index to look up (mutually exclusive with key)
        default: Value to return if not found (None if not specified)
        
    Returns:
        Any: Value at key/index or default
        
    Raises:
        TypeError: If both or neither key and index specified
    """

def __getitem__(self, key):
    """Get value by key.
    
    Args:
        key: Key to look up
        
    Returns:
        Any: Value associated with key
        
    Raises:
        KeyError: If key not found
    """

def index(self, key):
    """Get index of a key.
    
    Args:
        key: Key to find index of
        
    Returns:
        int: Index position of key
        
    Raises:
        KeyError: If key not found
    """

def key(self, index):
    """Get key at a specific index.
    
    Args:
        index: Index position
        
    Returns:
        Any: Key at the specified index
        
    Raises:
        IndexError: If index out of bounds
    """

Usage examples:

idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])

# Access by key
print(idict['b'])         # 2
print(idict.get(key='b')) # 2

# Access by index  
print(idict.get(index=1)) # 2 (value at index 1)
print(idict.key(1))       # 'b' (key at index 1)

# Get index of key
print(idict.index('c'))   # 2

# Safe access with defaults
print(idict.get(key='missing', default='not found'))  # 'not found'
print(idict.get(index=10, default='out of bounds'))   # 'out of bounds'

Standard Mapping Operations

All standard dictionary operations with order preservation.

def __setitem__(self, key, value):
    """Set key to value, preserving order for existing keys.
    
    Args:
        key: Key to set
        value: Value to associate with key
    """

def __delitem__(self, key):
    """Delete key and its value.
    
    Args:
        key: Key to delete
        
    Raises:
        KeyError: If key not found
    """

def __contains__(self, key):
    """Check if key exists.
    
    Args:
        key: Key to check
        
    Returns:
        bool: True if key exists
    """

def __len__(self):
    """Return number of key-value pairs.
    
    Returns:
        int: Number of items
    """

def __iter__(self):
    """Iterate over keys in insertion order."""

def keys(self):
    """Return view of keys in order."""

def values(self):
    """Return view of values in order."""

def items(self):
    """Return view of (key, value) pairs in order."""

Usage examples:

idict = IndexedDict()

# Add items - order preserved
idict['first'] = 1
idict['second'] = 2
idict['third'] = 3

# Update existing key - position unchanged
idict['second'] = 'two'

print(list(idict.keys()))    # ['first', 'second', 'third']
print(list(idict.values()))  # [1, 'two', 3]

# Standard operations
print('second' in idict)     # True
print(len(idict))           # 3

del idict['first']
print(list(idict.keys()))   # ['second', 'third']

Positional Removal Operations

Remove elements by key, index, or from ends with flexible options.

def pop(self, key=NOT_SET, index=NOT_SET, default=NOT_SET):
    """Remove and return value by key or index.
    
    Args:
        key: Key to pop (mutually exclusive with index)
        index: Index to pop (mutually exclusive with key, -1 for last)
        default: Value to return if key/index not found
        
    Returns:
        Any: Removed value or default
        
    Raises:
        KeyError: If key not found and no default
        IndexError: If index out of bounds and no default
        TypeError: If both or neither key and index specified
    """

def popitem(self, last=NOT_SET, *, key=NOT_SET, index=NOT_SET):
    """Remove and return (key, value) pair.
    
    Args:
        last: If True pop last item, if False pop first (for OrderedDict compatibility)
        key: Key to pop (keyword only)  
        index: Index to pop (keyword only)
        
    Returns:
        tuple: (key, value) pair that was removed
        
    Raises:
        KeyError: If dict empty or key not found
        IndexError: If index out of bounds
    """

def fast_pop(self, key=NOT_SET, index=NOT_SET):
    """Pop item quickly by swapping with last item.
    
    Changes order but runs in O(1) instead of O(n).
    
    Args:
        key: Key to pop (mutually exclusive with index)
        index: Index to pop (mutually exclusive with key)
        
    Returns:
        tuple: (popped_value, moved_index, moved_key, moved_value)
    """

Usage examples:

idict = IndexedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

# Pop by key
value = idict.pop(key='b')      # 2, dict now [('a', 1), ('c', 3), ('d', 4)]

# Pop by index  
value = idict.pop(index=1)      # 3, dict now [('a', 1), ('d', 4)]

# Pop last item (default)
value = idict.pop()             # 4, dict now [('a', 1)]

# Pop with default
value = idict.pop(key='missing', default='not found')  # 'not found'

# Pop item pairs
idict = IndexedDict([('x', 10), ('y', 20), ('z', 30)])
key, value = idict.popitem()         # ('z', 30) - last item
key, value = idict.popitem(last=False)  # ('x', 10) - first item

# Fast pop for better performance
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])
popped_val, moved_idx, moved_key, moved_val = idict.fast_pop(key='a')
# Result: popped_val=1, moved_idx=0, moved_key='c', moved_val=3
# Dict order changed: [('c', 3), ('b', 2)]

Order Manipulation

Reorder elements within the indexed dictionary.

def move_to_end(self, key=NOT_SET, index=NOT_SET, last=True):
    """Move existing element to end or beginning.
    
    Args:
        key: Key to move (mutually exclusive with index)
        index: Index to move (mutually exclusive with key)  
        last: If True move to end, if False move to beginning
        
    Raises:
        KeyError: If key not found
        IndexError: If index out of bounds
    """

Usage examples:

idict = IndexedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])

# Move to end
idict.move_to_end(key='b')      # [('a', 1), ('c', 3), ('d', 4), ('b', 2)]

# Move to beginning
idict.move_to_end(key='d', last=False)  # [('d', 4), ('a', 1), ('c', 3), ('b', 2)]

# Move by index
idict.move_to_end(index=1)      # [('d', 4), ('c', 3), ('b', 2), ('a', 1)]

Utility Operations

Copy, clear, and manage IndexedDict state.

def copy(self):
    """Return shallow copy of the IndexedDict.
    
    Returns:
        IndexedDict: New IndexedDict with same items
    """

def clear(self):
    """Remove all items from the IndexedDict."""

def update(self, other=None, **kwargs):
    """Update with key-value pairs from other or kwargs.
    
    Args:
        other: Mapping or iterable of pairs to update from
        **kwargs: Additional key-value pairs
    """

Usage examples:

original = IndexedDict([('a', 1), ('b', 2)])

# Copy
copy_dict = original.copy()
copy_dict['c'] = 3
print(len(original))    # 2 - original unchanged
print(len(copy_dict))   # 3

# Update
original.update({'c': 3, 'd': 4})
original.update(e=5, f=6)
print(len(original))    # 6

# Clear
original.clear()
print(len(original))    # 0

String Representations

Formatted output for debugging and display.

def __repr__(self):
    """Detailed representation showing internal structure."""

def __str__(self):
    """String representation as ordered dictionary."""

Usage examples:

idict = IndexedDict([('name', 'Alice'), ('age', 30), ('city', 'NYC')])

print(repr(idict))
# IndexedDict([('name', 'Alice'), ('age', 30), ('city', 'NYC')])

print(str(idict))  
# IndexedDict({'name': 'Alice', 'age': 30, 'city': 'NYC'})

Advanced Usage Patterns

Common patterns for effective IndexedDict usage.

# Building ordered mappings
config = IndexedDict()
config['database_url'] = 'postgresql://...'
config['redis_url'] = 'redis://...'
config['secret_key'] = 'abc123'

# Access by position for ordered processing
for i in range(len(config)):
    key = config.key(i)
    value = config[key]
    print(f"Setting {i+1}: {key} = {value}")

# Reorder based on priority
priority_keys = ['secret_key', 'database_url', 'redis_url']
ordered_config = IndexedDict()
for key in priority_keys:
    if key in config:
        ordered_config[key] = config[key]

# Index-based slicing simulation
def slice_indexed_dict(idict, start, stop):
    result = IndexedDict()
    for i in range(start, min(stop, len(idict))):
        key = idict.key(i)
        result[key] = idict[key]
    return result

partial = slice_indexed_dict(config, 1, 3)  # Items at indices 1-2

# LRU-like behavior with move_to_end
cache = IndexedDict()
def access_item(key):
    if key in cache:
        cache.move_to_end(key)  # Move to end on access
        return cache[key]
    return None

# Batch operations maintaining order
def batch_update_preserve_order(idict, updates):
    # Update existing keys in place, append new keys at end
    new_keys = []
    for key, value in updates.items():
        if key in idict:
            idict[key] = value
        else:
            new_keys.append((key, value))
    
    # Add new keys at end
    for key, value in new_keys:
        idict[key] = value

Error Handling and Edge Cases

Understanding IndexedDict behavior in various scenarios.

# Key vs index parameter validation
idict = IndexedDict([('a', 1), ('b', 2)])

try:
    # Must specify exactly one of key or index
    idict.get()  # TypeError - neither specified
except TypeError as e:
    print(f"Error: {e}")

try:
    idict.get(key='a', index=0)  # TypeError - both specified  
except TypeError as e:
    print(f"Error: {e}")

# Handling missing keys/indices gracefully
value = idict.get(key='missing', default='not found')
value = idict.get(index=10, default='out of bounds')

# Empty dict behavior  
empty = IndexedDict()
try:
    empty.popitem()  # KeyError
except KeyError:
    print("Cannot pop from empty IndexedDict")

# Negative index support
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])
last_value = idict.get(index=-1)  # 3
second_last = idict.get(index=-2)  # 2

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