from __future__ import annotations
from collections import deque
from collections.abc import Iterable
from typing import Collection, TypeVar
__all__ = ['OrderedSet']
_T = TypeVar('_T')
[docs]class OrderedSet(Collection[_T]):
"""A set whose elements are ordered"""
[docs] def __new__(cls, queue=()) -> OrderedSet[_T]:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> s = OrderedSet([1, 2, 3, 4, 3, 5])
>>> assert s is OrderedSet(s)
>>> assert s.as_set() == {1, 2, 3, 4, 5}
>>> assert s.as_tuple() == (1, 2, 3, 4, 5)
"""
if isinstance(queue, OrderedSet):
return queue
self = super().__new__(cls)
cls._queue: deque[_T]
cls._set: set[_T]
self._queue = deque()
self._set = set()
for x in queue:
self.append(x)
return self
def _add(self, element, add):
if element not in self._set:
self._set.add(element)
add(element)
[docs] def add(self, element: _T) -> None:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> x = OrderedSet()
>>> l = [3, 5, 2, 5, 6, 6]
>>> for e in l:
... x.add(e)
>>> assert x.as_set() == set(l)
>>> assert x.as_tuple() == (3, 5, 2, 6)
"""
self._add(element, self._queue.append)
[docs] def addleft(self, element: _T) -> None:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> x = OrderedSet()
>>> l = [3, 5, 2, 5, 6, 6]
>>> for e in l:
... x.addleft(e)
>>> assert x.as_set() == set(l)
>>> assert x.as_tuple() == (6, 2, 5, 3)
"""
self._add(element, self._queue.appendleft)
append = add
appendleft = addleft
[docs] def extend(self, other: Iterable[_T]):
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> o = OrderedSet([1, 2, 3])
>>> o.extend([3, 4, 5])
>>> assert o.as_tuple() == (1, 2, 3, 4, 5) """
for e in other:
self.append(e)
[docs] def extendleft(self, other: Iterable[_T]):
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> o = OrderedSet([1, 2, 3])
>>> o.extendleft([3, 4, 5])
>>> assert o.as_tuple() == (5, 4, 1, 2, 3)
"""
for e in other:
self.appendleft(e)
update = append
updateleft = appendleft
def _pop(self, pop):
try:
x = pop()
except IndexError:
raise IndexError(f'pop from empty {self.__class__.__name__}')
self._set.remove(x)
return x
[docs] def pop(self) -> _T:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> l = [1, 2, 3, 4]
>>> x = OrderedSet(l)
>>> l1 = []
>>> while x:
... l1.append(x.pop())
>>> assert list(reversed(l)) == l1
"""
return self._pop(self._queue.pop)
[docs] def popleft(self) -> _T:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> l = [1, 2, 3, 4]
>>> x = OrderedSet(l)
>>> l1 = []
>>> while x:
... l1.append(x.popleft())
>>> assert l == l1
"""
return self._pop(self._queue.popleft)
[docs] def as_tuple(self) -> tuple[_T, ...]:
return tuple(self._queue)
[docs] def as_set(self) -> frozenset[_T]:
return frozenset(self._set)
def __contains__(self, o: object) -> bool:
return o in self._set
def __iter__(self):
return iter(self._queue)
[docs] def __eq__(self, other: object) -> bool:
"""
.. testsetup::
from pathex.adts.containers.ordered_set import OrderedSet
>>> l = [1, 2, 3, 4]
>>> o = OrderedSet(l)
>>> assert o == [1, 2, 3, 4]
>>> assert o != [1, 2, 3, 4, 5]
>>> assert o != 25
"""
if isinstance(other, Iterable):
return self._queue == deque(other)
else:
return False
def __len__(self):
return len(self._queue)
def __bool__(self):
return self._set != set()
def __repr__(self): # pragma: no cover
return f'{self.__class__.__name__}({self._queue})'