Source code for glypy.utils.multimap

import logging
import json
from collections import defaultdict

logger = logging.getLogger(__name__)


def _str_dump_multimap(mm):  # pragma: no cover
    d = defaultdict(list)
    for k, v in mm.items():
        d[k].append(repr(v))
    return json.dumps(d, sort_keys=True, indent=4)


[docs]class MultiMap(object): __slots__ = ['contents', 'clean'] '''Implements a simple MultiMap data structure on top of a dictionary of lists''' def __init__(self, **kwargs): self.contents = defaultdict(list) for k, v in kwargs.items(): self.contents[k].append(v) self.invalidate() def invalidate(self): self.clean = False def __getitem__(self, key): return self.contents[key] def __setitem__(self, key, value): self.contents[key].append(value) self.invalidate() def pop(self, key, value): ''' Removes `value` from the collection of values stored at `key` and returns the `tuple` `(key, value)` Raises ------ IndexError KeyError ''' objs = self.contents[key] objs.pop(objs.index(value)) if len(objs) == 0: self.contents.pop(key) self.invalidate() return len(objs) def popv(self, value): for k in self: if value in self[k]: self.pop(k, value) return len(self[k]) self.invalidate() return None def __iter__(self): return iter(self.contents) def __contains__(self, key): return key in self.contents def keys(self): ''' Returns an iterator over the keys of :attr:`contents` An alias of :meth:`__iter__` ''' return iter(self) def values(self): ''' Returns an iterator over the values of :attr:`contents` ''' for k in self: for v in self[k]: yield v def items(self): ''' Returns an iterator over the items of :attr:`contents`. Each item takes the form of `(key, value)`. ''' for k in self: for v in self[k]: yield (k, v) def lists(self): return self.contents.items() def __len__(self): ''' Returns the number of items in :attr:`contents` ''' return sum(len(self[k]) for k in self) def __repr__(self): # pragma: no cover return _str_dump_multimap(self) def __eq__(self, other): if other is None: # pragma: no cover return False for a in self.keys(): if a in other: if self[a] != other[a]: return False else: if self[a] != []: return False for b in other.keys(): if b in self: continue else: if other[b] != []: return False # for a, b in izip_longest(self.items(), other.items()): # if a != b: # return False return True def __ne__(self, other): return not self == other def update(self, mapping): for k, v in mapping.items(): self[k] = v self.invalidate() def has_value(self, value): for v in self.values(): if v == value: return True return False def __reduce__(self): return self.__class__, (), self.__getstate__() def __getstate__(self): return self.contents def __setstate__(self, state): self.contents = state self.invalidate() def copy(self): new = self.__class__() for k, v in self.items(): new[k].extend(v) return new def clone(self): return self.copy()
[docs]class OrderedMultiMap(MultiMap): ''' Implements a simple MultiMap data structure on top of a dictionary of lists that remembers the order keys were first inserted in. ''' __slots__ = ['key_order', ] def __init__(self, **kwargs): self.contents = defaultdict(list) self.key_order = [] for k, v in kwargs.items(): if k not in self.key_order: self.key_order.append(k) self.contents[k].append(v) self.invalidate() def __iter__(self): ''' Returns an iterator over the keys of :attr:`contents` in the order they were added. ''' for key in self.key_order: yield key #: Alias of :meth:`__iter__` keys = __iter__ def values(self): for key in self.key_order: for v in self[key]: yield v def items(self): ''' As in :class:`MultiMap`, but items are yielded in the order their keys were first added. ''' for key in self.key_order: for v in self[key]: yield (key, v) def lists(self): for key in self.key_order: yield key, self[key] def reorder(self, new_order): assert set(new_order) == set(self.key_order) self.key_order = list(new_order) def __setitem__(self, key, value): if key not in self.key_order: self.key_order.append(key) self.contents[key].append(value) self.invalidate() def __repr__(self): # pragma: no cover return ''.join((repr(self.key_order), '\n', _str_dump_multimap(self))) def __getstate__(self): return self.contents, self.key_order def __setstate__(self, state): self.contents, self.key_order = state self.invalidate()