Extra Python Collections - bags (multisets) and setlists (ordered sets)
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
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.
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)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'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']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)]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)]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)) # 0Formatted 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'})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] = valueUnderstanding 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) # 2Install with Tessl CLI
npx tessl i tessl/pypi-collections-extended