Welcome to jaraco.collections documentation!#

For Enterprise

Professional support for jaraco.collections is available as part of the Tidelift Subscription. Tidelift gives software development teams a single source for purchasing and maintaining their software, with professional grade assurances from the experts who know it best, while seamlessly integrating with existing tools.

Learn more Request a Demo

class jaraco.collections.Accumulator(initial=0)#

Bases: object

class jaraco.collections.BijectiveMap(*args, **kwargs)#

Bases: dict

A Bijective Map (two-way mapping).

Implemented as a simple dictionary of 2x the size, mapping values back to keys.

Note, this implementation may be incomplete. If there’s not a test for your use case below, it’s likely to fail, so please test and send pull requests or patches for additional functionality needed.

>>> m = BijectiveMap()
>>> m['a'] = 'b'
>>> m == {'a': 'b', 'b': 'a'}
True
>>> print(m['b'])
a
>>> m['c'] = 'd'
>>> len(m)
2

Some weird things happen if you map an item to itself or overwrite a single key of a pair, so it’s disallowed.

>>> m['e'] = 'e'
Traceback (most recent call last):
ValueError: Key cannot map to itself
>>> m['d'] = 'e'
Traceback (most recent call last):
ValueError: Key/Value pairs may not overlap
>>> m['e'] = 'd'
Traceback (most recent call last):
ValueError: Key/Value pairs may not overlap
>>> print(m.pop('d'))
c
>>> 'c' in m
False
>>> m = BijectiveMap(dict(a='b'))
>>> len(m)
1
>>> print(m['b'])
a
>>> m = BijectiveMap()
>>> m.update(a='b')
>>> m['b']
'a'
>>> del m['b']
>>> len(m)
0
>>> 'a' in m
False
pop(k[, d]) v, remove specified key and return the corresponding value.#

If the key is not found, return the default if given; otherwise, raise a KeyError.

update([E, ]**F) None.  Update D from dict/iterable E and F.#

If E is present and has a .keys() method, then does: for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does: for k, v in E: D[k] = v In either case, this is followed by: for k in F: D[k] = F[k]

class jaraco.collections.DictAdapter(wrapped_ob)#

Bases: object

Provide a getitem interface for attributes of an object.

Let’s say you want to get at the string.lowercase property in a formatted string. It’s easy with DictAdapter.

>>> import string
>>> print("lowercase is %(ascii_lowercase)s" % DictAdapter(string))
lowercase is abcdefghijklmnopqrstuvwxyz
class jaraco.collections.DictStack(iterable=(), /)#

Bases: list, MutableMapping

A stack of dictionaries that behaves as a view on those dictionaries, giving preference to the last.

>>> stack = DictStack([dict(a=1, c=2), dict(b=2, a=2)])
>>> stack['a']
2
>>> stack['b']
2
>>> stack['c']
2
>>> len(stack)
3
>>> stack.push(dict(a=3))
>>> stack['a']
3
>>> stack['a'] = 4
>>> set(stack.keys()) == set(['a', 'b', 'c'])
True
>>> set(stack.items()) == set([('a', 4), ('b', 2), ('c', 2)])
True
>>> dict(**stack) == dict(stack) == dict(a=4, c=2, b=2)
True
>>> d = stack.pop()
>>> stack['a']
2
>>> d = stack.pop()
>>> stack['a']
1
>>> stack.get('b', None)
>>> 'c' in stack
True
>>> del stack['c']
>>> dict(stack)
{'a': 1}
pop(*args, **kwargs)#

Remove and return item at index (default last).

Raises IndexError if list is empty or index is out of range.

push(object, /)#

Append object to the end of the list.

class jaraco.collections.Enumeration(names, codes=None)#

Bases: ItemsAsAttributes, BijectiveMap

A convenient way to provide enumerated values

>>> e = Enumeration('a b c')
>>> e['a']
0
>>> e.a
0
>>> e[1]
'b'
>>> set(e.names) == set('abc')
True
>>> set(e.codes) == set(range(3))
True
>>> e.get('d') is None
True

Codes need not start with 0

>>> e = Enumeration('a b c', range(1, 4))
>>> e['a']
1
>>> e[3]
'c'
property codes#
property names#
class jaraco.collections.Everything#

Bases: object

A collection “containing” every possible thing.

>>> 'foo' in Everything()
True
>>> import random
>>> random.randint(1, 999) in Everything()
True
>>> random.choice([None, 'foo', 42, ('a', 'b', 'c')]) in Everything()
True
class jaraco.collections.FoldedCaseKeyedDict(*args, **kargs)#

Bases: KeyTransformingDict

A case-insensitive dictionary (keys are compared as insensitive if they are strings).

>>> d = FoldedCaseKeyedDict()
>>> d['heLlo'] = 'world'
>>> list(d.keys()) == ['heLlo']
True
>>> list(d.values()) == ['world']
True
>>> d['hello'] == 'world'
True
>>> 'hello' in d
True
>>> 'HELLO' in d
True
>>> print(repr(FoldedCaseKeyedDict({'heLlo': 'world'})))
{'heLlo': 'world'}
>>> d = FoldedCaseKeyedDict({'heLlo': 'world'})
>>> print(d['hello'])
world
>>> print(d['Hello'])
world
>>> list(d.keys())
['heLlo']
>>> d = FoldedCaseKeyedDict({'heLlo': 'world', 'Hello': 'world'})
>>> list(d.values())
['world']
>>> key, = d.keys()
>>> key in ['heLlo', 'Hello']
True
>>> del d['HELLO']
>>> d
{}

get should work

>>> d['Sumthin'] = 'else'
>>> d.get('SUMTHIN')
'else'
>>> d.get('OTHER', 'thing')
'thing'
>>> del d['sumthin']

setdefault should also work

>>> d['This'] = 'that'
>>> print(d.setdefault('this', 'other'))
that
>>> len(d)
1
>>> print(d['this'])
that
>>> print(d.setdefault('That', 'other'))
other
>>> print(d['THAT'])
other

Make it pop!

>>> print(d.pop('THAT'))
other

To retrieve the key in its originally-supplied form, use matching_key_for

>>> print(d.matching_key_for('this'))
This
>>> d.matching_key_for('missing')
Traceback (most recent call last):
...
KeyError: 'missing'
static transform_key(key)#
class jaraco.collections.FreezableDefaultDict#

Bases: defaultdict

Often it is desirable to prevent the mutation of a default dict after its initial construction, such as to prevent mutation during iteration.

>>> dd = FreezableDefaultDict(list)
>>> dd[0].append('1')
>>> dd.freeze()
>>> dd[1]
[]
>>> len(dd)
1
freeze()#
class jaraco.collections.FrozenDict(*args, **kwargs)#

Bases: Mapping, Hashable

An immutable mapping.

>>> a = FrozenDict(a=1, b=2)
>>> b = FrozenDict(a=1, b=2)
>>> a == b
True
>>> a == dict(a=1, b=2)
True
>>> dict(a=1, b=2) == a
True
>>> 'a' in a
True
>>> type(hash(a)) is type(0)
True
>>> set(iter(a)) == {'a', 'b'}
True
>>> len(a)
2
>>> a['a'] == a.get('a') == 1
True
>>> a['c'] = 3
Traceback (most recent call last):
...
TypeError: 'FrozenDict' object does not support item assignment
>>> a.update(y=3)
Traceback (most recent call last):
...
AttributeError: 'FrozenDict' object has no attribute 'update'

Copies should compare equal

>>> copy.copy(a) == a
True

Copies should be the same type

>>> isinstance(copy.copy(a), FrozenDict)
True

FrozenDict supplies .copy(), even though collections.abc.Mapping doesn’t demand it.

>>> a.copy() == a
True
>>> a.copy() is not a
True
copy()#

Return a shallow copy of self

get(k[, d]) D[k] if k in D, else d.  d defaults to None.#
class jaraco.collections.Greatest#

Bases: object

A value that is always greater than any other

>>> greatest = Greatest()
>>> 3 < greatest
True
>>> 3 > greatest
False
>>> greatest < 3
False
>>> greatest > 3
True
>>> greatest >= 3
True
>>> 'x' > greatest
False
>>> None > greatest
False
class jaraco.collections.IdentityOverrideMap#

Bases: dict

A dictionary that by default maps each key to itself, but otherwise acts like a normal dictionary.

>>> d = IdentityOverrideMap()
>>> d[42]
42
>>> d['speed'] = 'speedo'
>>> print(d['speed'])
speedo
class jaraco.collections.InstrumentedDict(data)#

Bases: UserDict

Instrument an existing dictionary with additional functionality, but always reference and mutate the original dictionary.

>>> orig = {'a': 1, 'b': 2}
>>> inst = InstrumentedDict(orig)
>>> inst['a']
1
>>> inst['c'] = 3
>>> orig['c']
3
>>> inst.keys() == orig.keys()
True
class jaraco.collections.ItemsAsAttributes#

Bases: object

Mix-in class to enable a mapping object to provide items as attributes.

>>> C = type('C', (dict, ItemsAsAttributes), dict())
>>> i = C()
>>> i['foo'] = 'bar'
>>> i.foo
'bar'

Natural attribute access takes precedence

>>> i.foo = 'henry'
>>> i.foo
'henry'

But as you might expect, the mapping functionality is preserved.

>>> i['foo']
'bar'

A normal attribute error should be raised if an attribute is requested that doesn’t exist.

>>> i.missing
Traceback (most recent call last):
...
AttributeError: 'C' object has no attribute 'missing'

It also works on dicts that customize __getitem__

>>> missing_func = lambda self, key: 'missing item'
>>> C = type(
...     'C',
...     (dict, ItemsAsAttributes),
...     dict(__missing__ = missing_func),
... )
>>> i = C()
>>> i.missing
'missing item'
>>> i.foo
'missing item'
class jaraco.collections.KeyTransformingDict(*args, **kargs)#

Bases: dict

A dict subclass that transforms the keys before they’re used. Subclasses may override the default transform_key to customize behavior.

get(key, *args, **kwargs)#

Return the value for key if key is in the dictionary, else default.

matching_key_for(key)#

Given a key, return the actual key stored in self that matches. Raise KeyError if the key isn’t found.

pop(k[, d]) v, remove specified key and return the corresponding value.#

If the key is not found, return the default if given; otherwise, raise a KeyError.

setdefault(key, *args, **kwargs)#

Insert key with a value of default if key is not in the dictionary.

Return the value for key if key is in the dictionary, else default.

static transform_key(key)#
class jaraco.collections.Least#

Bases: object

A value that is always lesser than any other

>>> least = Least()
>>> 3 < least
False
>>> 3 > least
True
>>> least < 3
True
>>> least <= 3
True
>>> least > 3
False
>>> 'x' > least
True
>>> None > least
True
class jaraco.collections.Mask(*args, **kwargs)#

Bases: Projection

The inverse of a Projection, masking out keys.

>>> sample = {'a': 1, 'b': 2, 'c': 3}
>>> msk = Mask(['a', 'c', 'd'], sample)
>>> dict(msk)
{'b': 2}
class jaraco.collections.Projection(keys: Callable | Container | Iterable | Pattern, space: Mapping)#

Bases: Mapping

Project a set of keys over a mapping

>>> sample = {'a': 1, 'b': 2, 'c': 3}
>>> prj = Projection(['a', 'c', 'd'], sample)
>>> dict(prj)
{'a': 1, 'c': 3}

Projection also accepts an iterable or callable or pattern.

>>> iter_prj = Projection(iter('acd'), sample)
>>> call_prj = Projection(lambda k: ord(k) in (97, 99, 100), sample)
>>> pat_prj = Projection(re.compile(r'[acd]'), sample)
>>> prj == iter_prj == call_prj == pat_prj
True

Keys should only appear if they were specified and exist in the space. Order is retained.

>>> list(prj)
['a', 'c']

Attempting to access a key not in the projection results in a KeyError.

>>> prj['b']
Traceback (most recent call last):
...
KeyError: 'b'

Use the projection to update another dict.

>>> target = {'a': 2, 'b': 2}
>>> target.update(prj)
>>> target
{'a': 1, 'b': 2, 'c': 3}

Projection keeps a reference to the original dict, so modifying the original dict may modify the Projection.

>>> del sample['a']
>>> dict(prj)
{'c': 3}
class jaraco.collections.RangeMap(source, sort_params={}, key_match_comparator=operator.le)#

Bases: dict

A dictionary-like object that uses the keys as bounds for a range. Inclusion of the value for that range is determined by the key_match_comparator, which defaults to less-than-or-equal. A value is returned for a key if it is the first key that matches in the sorted list of keys.

One may supply keyword parameters to be passed to the sort function used to sort keys (i.e. key, reverse) as sort_params.

Create a map that maps 1-3 -> ‘a’, 4-6 -> ‘b’

>>> r = RangeMap({3: 'a', 6: 'b'})  # boy, that was easy
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')

Even float values should work so long as the comparison operator supports it.

>>> r[4.5]
'b'

Notice that the way rangemap is defined, it must be open-ended on one side.

>>> r[0]
'a'
>>> r[-1]
'a'

One can close the open-end of the RangeMap by using undefined_value

>>> r = RangeMap({0: RangeMap.undefined_value, 3: 'a', 6: 'b'})
>>> r[0]
Traceback (most recent call last):
...
KeyError: 0

One can get the first or last elements in the range by using RangeMap.Item

>>> last_item = RangeMap.Item(-1)
>>> r[last_item]
'b'

.last_item is a shortcut for Item(-1)

>>> r[RangeMap.last_item]
'b'

Sometimes it’s useful to find the bounds for a RangeMap

>>> r.bounds()
(0, 6)

RangeMap supports .get(key, default)

>>> r.get(0, 'not found')
'not found'
>>> r.get(7, 'not found')
'not found'

One often wishes to define the ranges by their left-most values, which requires use of sort params and a key_match_comparator.

>>> r = RangeMap({1: 'a', 4: 'b'},
...     sort_params=dict(reverse=True),
...     key_match_comparator=operator.ge)
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')

That wasn’t nearly as easy as before, so an alternate constructor is provided:

>>> r = RangeMap.left({1: 'a', 4: 'b', 7: RangeMap.undefined_value})
>>> r[1], r[2], r[3], r[4], r[5], r[6]
('a', 'a', 'a', 'b', 'b', 'b')
class Item#

Bases: int

RangeMap Item

bounds()#
first_item = 0#
get(key, default=None)#

Return the value for key if key is in the dictionary, else default. If default is not given, it defaults to None, so that this method never raises a KeyError.

last_item = -1#
classmethod left(source)#
undefined_value = <jaraco.collections.RangeValueUndefined object>#
class jaraco.collections.WeightedLookup(*args, **kwargs)#

Bases: RangeMap

Given parameters suitable for a dict representing keys and a weighted proportion, return a RangeMap representing spans of values proportial to the weights:

>>> even = WeightedLookup(a=1, b=1)

[0, 1) -> a [1, 2) -> b

>>> lk = WeightedLookup(a=1, b=2)

[0, 1) -> a [1, 3) -> b

>>> lk[.5]
'a'
>>> lk[1.5]
'b'

Adds .random() to select a random weighted value:

>>> lk.random() in ['a', 'b']
True
>>> choices = [lk.random() for x in range(1000)]

Statistically speaking, choices should be .5 a:b >>> ratio = choices.count(‘a’) / choices.count(‘b’) >>> .4 < ratio < .6 True

random()#
jaraco.collections.dict_map(function, dictionary)#

Return a new dict with function applied to values of dictionary.

>>> dict_map(lambda x: x+1, dict(a=1, b=2))
{'a': 2, 'b': 3}
jaraco.collections.invert_map(map)#

Given a dictionary, return another dictionary with keys and values switched. If any of the values resolve to the same key, raises a ValueError.

>>> numbers = dict(a=1, b=2, c=3)
>>> letters = invert_map(numbers)
>>> letters[1]
'a'
>>> numbers['d'] = 3
>>> invert_map(numbers)
Traceback (most recent call last):
...
ValueError: Key conflict in inverted mapping
jaraco.collections.pop_all(items)#

Clear items in place and return a copy of items.

>>> items = [1, 2, 3]
>>> popped = pop_all(items)
>>> popped is items
False
>>> popped
[1, 2, 3]
>>> items
[]
jaraco.collections.sorted_items(d, key=__identity, reverse=False)#

Return the items of the dictionary sorted by the keys.

>>> sample = dict(foo=20, bar=42, baz=10)
>>> tuple(sorted_items(sample))
(('bar', 42), ('baz', 10), ('foo', 20))
>>> reverse_string = lambda s: ''.join(reversed(s))
>>> tuple(sorted_items(sample, key=reverse_string))
(('foo', 20), ('bar', 42), ('baz', 10))
>>> tuple(sorted_items(sample, reverse=True))
(('foo', 20), ('baz', 10), ('bar', 42))

Indices and tables#