0
# Indexed Dictionaries
1
2
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.
3
4
## Capabilities
5
6
### IndexedDict Construction
7
8
Create ordered dictionaries from various input formats with both key and index access.
9
10
```python { .api }
11
class IndexedDict:
12
def __init__(self, iterable=None, **kwargs):
13
"""Create an IndexedDict.
14
15
Args:
16
iterable: Mapping or iterable of (key, value) pairs
17
**kwargs: Additional key-value pairs
18
"""
19
```
20
21
Usage examples:
22
```python
23
from collections_extended import IndexedDict
24
25
# Create empty
26
idict = IndexedDict()
27
28
# Create from dictionary
29
idict = IndexedDict({'a': 1, 'b': 2, 'c': 3})
30
31
# Create from iterable of pairs
32
idict = IndexedDict([('x', 10), ('y', 20), ('z', 30)])
33
34
# Create with keyword arguments
35
idict = IndexedDict(name='Alice', age=30, city='NYC')
36
37
# Mix approaches
38
idict = IndexedDict({'a': 1}, b=2, c=3)
39
```
40
41
### Dual Access Patterns
42
43
Access elements by either key or index position efficiently.
44
45
```python { .api }
46
def get(self, key=NOT_SET, index=NOT_SET, default=NOT_SET):
47
"""Get value by key or index.
48
49
Args:
50
key: Key to look up (mutually exclusive with index)
51
index: Index to look up (mutually exclusive with key)
52
default: Value to return if not found (None if not specified)
53
54
Returns:
55
Any: Value at key/index or default
56
57
Raises:
58
TypeError: If both or neither key and index specified
59
"""
60
61
def __getitem__(self, key):
62
"""Get value by key.
63
64
Args:
65
key: Key to look up
66
67
Returns:
68
Any: Value associated with key
69
70
Raises:
71
KeyError: If key not found
72
"""
73
74
def index(self, key):
75
"""Get index of a key.
76
77
Args:
78
key: Key to find index of
79
80
Returns:
81
int: Index position of key
82
83
Raises:
84
KeyError: If key not found
85
"""
86
87
def key(self, index):
88
"""Get key at a specific index.
89
90
Args:
91
index: Index position
92
93
Returns:
94
Any: Key at the specified index
95
96
Raises:
97
IndexError: If index out of bounds
98
"""
99
```
100
101
Usage examples:
102
```python
103
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])
104
105
# Access by key
106
print(idict['b']) # 2
107
print(idict.get(key='b')) # 2
108
109
# Access by index
110
print(idict.get(index=1)) # 2 (value at index 1)
111
print(idict.key(1)) # 'b' (key at index 1)
112
113
# Get index of key
114
print(idict.index('c')) # 2
115
116
# Safe access with defaults
117
print(idict.get(key='missing', default='not found')) # 'not found'
118
print(idict.get(index=10, default='out of bounds')) # 'out of bounds'
119
```
120
121
### Standard Mapping Operations
122
123
All standard dictionary operations with order preservation.
124
125
```python { .api }
126
def __setitem__(self, key, value):
127
"""Set key to value, preserving order for existing keys.
128
129
Args:
130
key: Key to set
131
value: Value to associate with key
132
"""
133
134
def __delitem__(self, key):
135
"""Delete key and its value.
136
137
Args:
138
key: Key to delete
139
140
Raises:
141
KeyError: If key not found
142
"""
143
144
def __contains__(self, key):
145
"""Check if key exists.
146
147
Args:
148
key: Key to check
149
150
Returns:
151
bool: True if key exists
152
"""
153
154
def __len__(self):
155
"""Return number of key-value pairs.
156
157
Returns:
158
int: Number of items
159
"""
160
161
def __iter__(self):
162
"""Iterate over keys in insertion order."""
163
164
def keys(self):
165
"""Return view of keys in order."""
166
167
def values(self):
168
"""Return view of values in order."""
169
170
def items(self):
171
"""Return view of (key, value) pairs in order."""
172
```
173
174
Usage examples:
175
```python
176
idict = IndexedDict()
177
178
# Add items - order preserved
179
idict['first'] = 1
180
idict['second'] = 2
181
idict['third'] = 3
182
183
# Update existing key - position unchanged
184
idict['second'] = 'two'
185
186
print(list(idict.keys())) # ['first', 'second', 'third']
187
print(list(idict.values())) # [1, 'two', 3]
188
189
# Standard operations
190
print('second' in idict) # True
191
print(len(idict)) # 3
192
193
del idict['first']
194
print(list(idict.keys())) # ['second', 'third']
195
```
196
197
### Positional Removal Operations
198
199
Remove elements by key, index, or from ends with flexible options.
200
201
```python { .api }
202
def pop(self, key=NOT_SET, index=NOT_SET, default=NOT_SET):
203
"""Remove and return value by key or index.
204
205
Args:
206
key: Key to pop (mutually exclusive with index)
207
index: Index to pop (mutually exclusive with key, -1 for last)
208
default: Value to return if key/index not found
209
210
Returns:
211
Any: Removed value or default
212
213
Raises:
214
KeyError: If key not found and no default
215
IndexError: If index out of bounds and no default
216
TypeError: If both or neither key and index specified
217
"""
218
219
def popitem(self, last=NOT_SET, *, key=NOT_SET, index=NOT_SET):
220
"""Remove and return (key, value) pair.
221
222
Args:
223
last: If True pop last item, if False pop first (for OrderedDict compatibility)
224
key: Key to pop (keyword only)
225
index: Index to pop (keyword only)
226
227
Returns:
228
tuple: (key, value) pair that was removed
229
230
Raises:
231
KeyError: If dict empty or key not found
232
IndexError: If index out of bounds
233
"""
234
235
def fast_pop(self, key=NOT_SET, index=NOT_SET):
236
"""Pop item quickly by swapping with last item.
237
238
Changes order but runs in O(1) instead of O(n).
239
240
Args:
241
key: Key to pop (mutually exclusive with index)
242
index: Index to pop (mutually exclusive with key)
243
244
Returns:
245
tuple: (popped_value, moved_index, moved_key, moved_value)
246
"""
247
```
248
249
Usage examples:
250
```python
251
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
252
253
# Pop by key
254
value = idict.pop(key='b') # 2, dict now [('a', 1), ('c', 3), ('d', 4)]
255
256
# Pop by index
257
value = idict.pop(index=1) # 3, dict now [('a', 1), ('d', 4)]
258
259
# Pop last item (default)
260
value = idict.pop() # 4, dict now [('a', 1)]
261
262
# Pop with default
263
value = idict.pop(key='missing', default='not found') # 'not found'
264
265
# Pop item pairs
266
idict = IndexedDict([('x', 10), ('y', 20), ('z', 30)])
267
key, value = idict.popitem() # ('z', 30) - last item
268
key, value = idict.popitem(last=False) # ('x', 10) - first item
269
270
# Fast pop for better performance
271
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])
272
popped_val, moved_idx, moved_key, moved_val = idict.fast_pop(key='a')
273
# Result: popped_val=1, moved_idx=0, moved_key='c', moved_val=3
274
# Dict order changed: [('c', 3), ('b', 2)]
275
```
276
277
### Order Manipulation
278
279
Reorder elements within the indexed dictionary.
280
281
```python { .api }
282
def move_to_end(self, key=NOT_SET, index=NOT_SET, last=True):
283
"""Move existing element to end or beginning.
284
285
Args:
286
key: Key to move (mutually exclusive with index)
287
index: Index to move (mutually exclusive with key)
288
last: If True move to end, if False move to beginning
289
290
Raises:
291
KeyError: If key not found
292
IndexError: If index out of bounds
293
"""
294
```
295
296
Usage examples:
297
```python
298
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3), ('d', 4)])
299
300
# Move to end
301
idict.move_to_end(key='b') # [('a', 1), ('c', 3), ('d', 4), ('b', 2)]
302
303
# Move to beginning
304
idict.move_to_end(key='d', last=False) # [('d', 4), ('a', 1), ('c', 3), ('b', 2)]
305
306
# Move by index
307
idict.move_to_end(index=1) # [('d', 4), ('c', 3), ('b', 2), ('a', 1)]
308
```
309
310
### Utility Operations
311
312
Copy, clear, and manage IndexedDict state.
313
314
```python { .api }
315
def copy(self):
316
"""Return shallow copy of the IndexedDict.
317
318
Returns:
319
IndexedDict: New IndexedDict with same items
320
"""
321
322
def clear(self):
323
"""Remove all items from the IndexedDict."""
324
325
def update(self, other=None, **kwargs):
326
"""Update with key-value pairs from other or kwargs.
327
328
Args:
329
other: Mapping or iterable of pairs to update from
330
**kwargs: Additional key-value pairs
331
"""
332
```
333
334
Usage examples:
335
```python
336
original = IndexedDict([('a', 1), ('b', 2)])
337
338
# Copy
339
copy_dict = original.copy()
340
copy_dict['c'] = 3
341
print(len(original)) # 2 - original unchanged
342
print(len(copy_dict)) # 3
343
344
# Update
345
original.update({'c': 3, 'd': 4})
346
original.update(e=5, f=6)
347
print(len(original)) # 6
348
349
# Clear
350
original.clear()
351
print(len(original)) # 0
352
```
353
354
### String Representations
355
356
Formatted output for debugging and display.
357
358
```python { .api }
359
def __repr__(self):
360
"""Detailed representation showing internal structure."""
361
362
def __str__(self):
363
"""String representation as ordered dictionary."""
364
```
365
366
Usage examples:
367
```python
368
idict = IndexedDict([('name', 'Alice'), ('age', 30), ('city', 'NYC')])
369
370
print(repr(idict))
371
# IndexedDict([('name', 'Alice'), ('age', 30), ('city', 'NYC')])
372
373
print(str(idict))
374
# IndexedDict({'name': 'Alice', 'age': 30, 'city': 'NYC'})
375
```
376
377
### Advanced Usage Patterns
378
379
Common patterns for effective IndexedDict usage.
380
381
```python
382
# Building ordered mappings
383
config = IndexedDict()
384
config['database_url'] = 'postgresql://...'
385
config['redis_url'] = 'redis://...'
386
config['secret_key'] = 'abc123'
387
388
# Access by position for ordered processing
389
for i in range(len(config)):
390
key = config.key(i)
391
value = config[key]
392
print(f"Setting {i+1}: {key} = {value}")
393
394
# Reorder based on priority
395
priority_keys = ['secret_key', 'database_url', 'redis_url']
396
ordered_config = IndexedDict()
397
for key in priority_keys:
398
if key in config:
399
ordered_config[key] = config[key]
400
401
# Index-based slicing simulation
402
def slice_indexed_dict(idict, start, stop):
403
result = IndexedDict()
404
for i in range(start, min(stop, len(idict))):
405
key = idict.key(i)
406
result[key] = idict[key]
407
return result
408
409
partial = slice_indexed_dict(config, 1, 3) # Items at indices 1-2
410
411
# LRU-like behavior with move_to_end
412
cache = IndexedDict()
413
def access_item(key):
414
if key in cache:
415
cache.move_to_end(key) # Move to end on access
416
return cache[key]
417
return None
418
419
# Batch operations maintaining order
420
def batch_update_preserve_order(idict, updates):
421
# Update existing keys in place, append new keys at end
422
new_keys = []
423
for key, value in updates.items():
424
if key in idict:
425
idict[key] = value
426
else:
427
new_keys.append((key, value))
428
429
# Add new keys at end
430
for key, value in new_keys:
431
idict[key] = value
432
```
433
434
### Error Handling and Edge Cases
435
436
Understanding IndexedDict behavior in various scenarios.
437
438
```python
439
# Key vs index parameter validation
440
idict = IndexedDict([('a', 1), ('b', 2)])
441
442
try:
443
# Must specify exactly one of key or index
444
idict.get() # TypeError - neither specified
445
except TypeError as e:
446
print(f"Error: {e}")
447
448
try:
449
idict.get(key='a', index=0) # TypeError - both specified
450
except TypeError as e:
451
print(f"Error: {e}")
452
453
# Handling missing keys/indices gracefully
454
value = idict.get(key='missing', default='not found')
455
value = idict.get(index=10, default='out of bounds')
456
457
# Empty dict behavior
458
empty = IndexedDict()
459
try:
460
empty.popitem() # KeyError
461
except KeyError:
462
print("Cannot pop from empty IndexedDict")
463
464
# Negative index support
465
idict = IndexedDict([('a', 1), ('b', 2), ('c', 3)])
466
last_value = idict.get(index=-1) # 3
467
second_last = idict.get(index=-2) # 2
468
```