Refactored cpl-query Iterable & Enumerable #129
This commit is contained in:
parent
e8ae635c88
commit
f0f79e7e3b
34
src/cpl_query/base/ordered_queryable.py
Normal file
34
src/cpl_query/base/ordered_queryable.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from collections.abc import Callable
|
||||||
|
|
||||||
|
from cpl_query.base.ordered_queryable_abc import OrderedQueryableABC
|
||||||
|
from cpl_query.exceptions import ArgumentNoneException, ExceptionArgument
|
||||||
|
from cpl_query.iterable.iterable import Iterable
|
||||||
|
|
||||||
|
|
||||||
|
class OrderedQueryable(OrderedQueryableABC):
|
||||||
|
r"""Implementation of :class: `cpl_query.extension.Iterable` `cpl_query.extension.OrderedIterableABC`
|
||||||
|
"""
|
||||||
|
|
||||||
|
def __init__(self, _t: type, _values: Iterable = None, _func: Callable = None):
|
||||||
|
OrderedQueryableABC.__init__(self, _t, _values, _func)
|
||||||
|
|
||||||
|
def then_by(self: OrderedQueryableABC, _func: Callable) -> OrderedQueryableABC:
|
||||||
|
if self is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.list)
|
||||||
|
|
||||||
|
if _func is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.func)
|
||||||
|
|
||||||
|
self._funcs.append(_func)
|
||||||
|
|
||||||
|
return OrderedQueryable(self.type, sorted(self, key=lambda *args: [f(*args) for f in self._funcs]), _func)
|
||||||
|
|
||||||
|
def then_by_descending(self: OrderedQueryableABC, _func: Callable) -> OrderedQueryableABC:
|
||||||
|
if self is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.list)
|
||||||
|
|
||||||
|
if _func is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.func)
|
||||||
|
|
||||||
|
self._funcs.append(_func)
|
||||||
|
return OrderedQueryable(self.type, sorted(self, key=lambda *args: [f(*args) for f in self._funcs], reverse=True), _func)
|
@ -2,20 +2,20 @@ from abc import abstractmethod
|
|||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from typing import Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from cpl_query.iterable.iterable_abc import IterableABC
|
from cpl_query.base.queryable_abc import QueryableABC
|
||||||
|
|
||||||
|
|
||||||
class OrderedIterableABC(IterableABC):
|
class OrderedQueryableABC(QueryableABC):
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, _t: type, _func: Callable = None, _values: Iterable = None):
|
def __init__(self, _t: type, _values: Iterable = None, _func: Callable = None):
|
||||||
IterableABC.__init__(self, _t, _values)
|
QueryableABC.__init__(self, _t, _values)
|
||||||
self._funcs: list[Callable] = []
|
self._funcs: list[Callable] = []
|
||||||
if _func is not None:
|
if _func is not None:
|
||||||
self._funcs.append(_func)
|
self._funcs.append(_func)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def then_by(self, func: Callable) -> 'OrderedIterableABC':
|
def then_by(self, func: Callable) -> 'OrderedQueryableABC':
|
||||||
r"""Sorts OrderedList in ascending order by function
|
r"""Sorts OrderedList in ascending order by function
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -29,7 +29,7 @@ class OrderedIterableABC(IterableABC):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def then_by_descending(self, func: Callable) -> 'OrderedIterableABC':
|
def then_by_descending(self, func: Callable) -> 'OrderedQueryableABC':
|
||||||
r"""Sorts OrderedList in descending order by function
|
r"""Sorts OrderedList in descending order by function
|
||||||
|
|
||||||
Parameter
|
Parameter
|
@ -1,10 +1,19 @@
|
|||||||
from abc import abstractmethod, ABC
|
|
||||||
from typing import Optional, Callable, Union
|
from typing import Optional, Callable, Union
|
||||||
|
|
||||||
|
from cpl_query._helper import is_number
|
||||||
|
from cpl_query.base.sequence_abc import SequenceABC
|
||||||
|
from cpl_query.exceptions import InvalidTypeException, ArgumentNoneException, ExceptionArgument, IndexOutOfRangeException
|
||||||
|
|
||||||
class QueryableABC(ABC):
|
|
||||||
|
|
||||||
@abstractmethod
|
def _default_lambda(x: object):
|
||||||
|
return x
|
||||||
|
|
||||||
|
|
||||||
|
class QueryableABC(SequenceABC):
|
||||||
|
|
||||||
|
def __init__(self, t: type = None, values: list = None):
|
||||||
|
SequenceABC.__init__(self, t, values)
|
||||||
|
|
||||||
def all(self, _func: Callable = None) -> bool:
|
def all(self, _func: Callable = None) -> bool:
|
||||||
r"""Checks if every element of list equals result found by function
|
r"""Checks if every element of list equals result found by function
|
||||||
|
|
||||||
@ -17,9 +26,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
return self.count(_func) == self.count()
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def any(self, _func: Callable = None) -> bool:
|
def any(self, _func: Callable = None) -> bool:
|
||||||
r"""Checks if list contains result found by function
|
r"""Checks if list contains result found by function
|
||||||
|
|
||||||
@ -32,9 +43,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
return self.where(_func).count() > 0
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def average(self, _func: Callable = None) -> Union[int, float, complex]:
|
def average(self, _func: Callable = None) -> Union[int, float, complex]:
|
||||||
r"""Returns average value of list
|
r"""Returns average value of list
|
||||||
|
|
||||||
@ -47,10 +60,12 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Union[int, float, complex]
|
Union[int, float, complex]
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None and not is_number(self.type):
|
||||||
|
raise InvalidTypeException()
|
||||||
|
|
||||||
@abstractmethod
|
return self.sum(_func) / self.count()
|
||||||
def contains(self, value: object) -> bool:
|
|
||||||
|
def contains(self, _value: object) -> bool:
|
||||||
r"""Checks if list contains value given by function
|
r"""Checks if list contains value given by function
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
@ -62,9 +77,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
bool
|
bool
|
||||||
"""
|
"""
|
||||||
pass
|
if _value is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.value)
|
||||||
|
|
||||||
|
return self.where(lambda x: x == _value).count() > 0
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def count(self, _func: Callable = None) -> int:
|
def count(self, _func: Callable = None) -> int:
|
||||||
r"""Returns length of list or count of found elements
|
r"""Returns length of list or count of found elements
|
||||||
|
|
||||||
@ -77,9 +94,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
int
|
int
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
return self.__len__()
|
||||||
|
|
||||||
|
return self.where(_func).__len__()
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def distinct(self, _func: Callable = None) -> 'QueryableABC':
|
def distinct(self, _func: Callable = None) -> 'QueryableABC':
|
||||||
r"""Returns list without redundancies
|
r"""Returns list without redundancies
|
||||||
|
|
||||||
@ -92,39 +111,62 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
@abstractmethod
|
result = []
|
||||||
def element_at(self, index: int) -> any:
|
known_values = []
|
||||||
|
for element in self:
|
||||||
|
value = _func(element)
|
||||||
|
if value in known_values:
|
||||||
|
continue
|
||||||
|
|
||||||
|
known_values.append(value)
|
||||||
|
result.append(element)
|
||||||
|
|
||||||
|
return type(self)(self._type, result)
|
||||||
|
|
||||||
|
def element_at(self, _index: int) -> any:
|
||||||
r"""Returns element at given index
|
r"""Returns element at given index
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Value at index: any
|
Value at _index: any
|
||||||
"""
|
"""
|
||||||
pass
|
if _index is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.index)
|
||||||
|
|
||||||
@abstractmethod
|
result = self[_index]
|
||||||
def element_at_or_default(self, index: int) -> Optional[any]:
|
if result is None:
|
||||||
|
raise IndexOutOfRangeException
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def element_at_or_default(self, _index: int) -> Optional[any]:
|
||||||
r"""Returns element at given index or None
|
r"""Returns element at given index or None
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Value at index: Optional[any]
|
Value at _index: Optional[any]
|
||||||
"""
|
"""
|
||||||
pass
|
if _index is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.index)
|
||||||
|
|
||||||
|
try:
|
||||||
|
return self[_index]
|
||||||
|
except IndexError:
|
||||||
|
return None
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def first(self) -> any:
|
def first(self) -> any:
|
||||||
r"""Returns first element
|
r"""Returns first element
|
||||||
|
|
||||||
@ -132,9 +174,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
First element of list: any
|
First element of list: any
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) == 0:
|
||||||
|
raise IndexOutOfRangeException()
|
||||||
|
|
||||||
|
return self[0]
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def first_or_default(self) -> any:
|
def first_or_default(self) -> any:
|
||||||
r"""Returns first element or None
|
r"""Returns first element or None
|
||||||
|
|
||||||
@ -142,9 +186,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
First element of list: Optional[any]
|
First element of list: Optional[any]
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self[0]
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def for_each(self, _func: Callable = None):
|
def for_each(self, _func: Callable = None):
|
||||||
r"""Runs given function for each element of list
|
r"""Runs given function for each element of list
|
||||||
|
|
||||||
@ -153,7 +199,11 @@ class QueryableABC(ABC):
|
|||||||
func: :class: `Callable`
|
func: :class: `Callable`
|
||||||
function to call
|
function to call
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is not None:
|
||||||
|
for element in self:
|
||||||
|
_func(element)
|
||||||
|
|
||||||
|
return self
|
||||||
|
|
||||||
def group_by(self, _func: Callable = None) -> 'QueryableABC':
|
def group_by(self, _func: Callable = None) -> 'QueryableABC':
|
||||||
r"""Groups by func
|
r"""Groups by func
|
||||||
@ -162,9 +212,23 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Grouped list[list[any]]: any
|
Grouped list[list[any]]: any
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
groups = {}
|
||||||
|
|
||||||
|
for v in self:
|
||||||
|
value = _func(v)
|
||||||
|
if v not in groups:
|
||||||
|
groups[value] = []
|
||||||
|
|
||||||
|
groups[value].append(v)
|
||||||
|
|
||||||
|
v = []
|
||||||
|
for g in groups.values():
|
||||||
|
v.append(type(self)(None, g))
|
||||||
|
x = type(self)(type(self), v)
|
||||||
|
return x
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def last(self) -> any:
|
def last(self) -> any:
|
||||||
r"""Returns last element
|
r"""Returns last element
|
||||||
|
|
||||||
@ -172,9 +236,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Last element of list: any
|
Last element of list: any
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) == 0:
|
||||||
|
raise IndexOutOfRangeException()
|
||||||
|
|
||||||
|
return self[len(self) - 1]
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def last_or_default(self) -> any:
|
def last_or_default(self) -> any:
|
||||||
r"""Returns last element or None
|
r"""Returns last element or None
|
||||||
|
|
||||||
@ -182,9 +248,11 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Last element of list: Optional[any]
|
Last element of list: Optional[any]
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self[len(self) - 1]
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def max(self, _func: Callable = None) -> Union[int, float, complex]:
|
def max(self, _func: Callable = None) -> Union[int, float, complex]:
|
||||||
r"""Returns the highest value
|
r"""Returns the highest value
|
||||||
|
|
||||||
@ -197,19 +265,33 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Union[int, float, complex]
|
Union[int, float, complex]
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None and not is_number(self.type):
|
||||||
|
raise InvalidTypeException()
|
||||||
|
|
||||||
@abstractmethod
|
if _func is None:
|
||||||
def median(self) -> Union[int, float]:
|
_func = _default_lambda
|
||||||
|
|
||||||
|
return _func(max(self, key=_func))
|
||||||
|
|
||||||
|
def median(self, _func=None) -> Union[int, float]:
|
||||||
r"""Return the median value of data elements
|
r"""Return the median value of data elements
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
Union[int, float]
|
Union[int, float]
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
result = self.order_by(_func).select(_func).to_list()
|
||||||
|
length = len(result)
|
||||||
|
i = int(length / 2)
|
||||||
|
return (
|
||||||
|
result[i]
|
||||||
|
if length % 2 == 1
|
||||||
|
else (float(result[i - 1]) + float(result[i])) / float(2)
|
||||||
|
)
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def min(self, _func: Callable = None) -> Union[int, float, complex]:
|
def min(self, _func: Callable = None) -> Union[int, float, complex]:
|
||||||
r"""Returns the lowest value
|
r"""Returns the lowest value
|
||||||
|
|
||||||
@ -222,9 +304,14 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Union[int, float, complex]
|
Union[int, float, complex]
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None and not is_number(self.type):
|
||||||
|
raise InvalidTypeException()
|
||||||
|
|
||||||
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
return _func(min(self, key=_func))
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def order_by(self, _func: Callable = None) -> 'QueryableABC':
|
def order_by(self, _func: Callable = None) -> 'QueryableABC':
|
||||||
r"""Sorts elements by function in ascending order
|
r"""Sorts elements by function in ascending order
|
||||||
|
|
||||||
@ -237,9 +324,12 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
from cpl_query.base.ordered_queryable import OrderedQueryable
|
||||||
|
return OrderedQueryable(self.type, sorted(self, key=_func), _func)
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def order_by_descending(self, _func: Callable = None) -> 'QueryableABC':
|
def order_by_descending(self, _func: Callable = None) -> 'QueryableABC':
|
||||||
r"""Sorts elements by function in descending order
|
r"""Sorts elements by function in descending order
|
||||||
|
|
||||||
@ -252,9 +342,12 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
from cpl_query.base.ordered_queryable import OrderedQueryable
|
||||||
|
return OrderedQueryable(self.type, sorted(self, key=_func, reverse=True), _func)
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def reverse(self) -> 'QueryableABC':
|
def reverse(self) -> 'QueryableABC':
|
||||||
r"""Reverses list
|
r"""Reverses list
|
||||||
|
|
||||||
@ -262,29 +355,31 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
return type(self)(self._type, list(reversed(self)))
|
||||||
|
|
||||||
@abstractmethod
|
def select(self, _func: Callable) -> 'QueryableABC':
|
||||||
def select(self, _f: Callable) -> 'QueryableABC':
|
|
||||||
r"""Formats each element of list to a given format
|
r"""Formats each element of list to a given format
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
@abstractmethod
|
return type(self)(any, [_func(_o) for _o in self])
|
||||||
def select_many(self, _f: Callable) -> 'QueryableABC':
|
|
||||||
|
def select_many(self, _func: Callable) -> 'QueryableABC':
|
||||||
r"""Flattens resulting lists to one
|
r"""Flattens resulting lists to one
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
# The line below is pain. I don't understand anything of it...
|
||||||
|
# written on 09.11.2022 by Sven Heidemann
|
||||||
|
return type(self)(any, [_a for _o in self for _a in _func(_o)])
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def single(self) -> any:
|
def single(self) -> any:
|
||||||
r"""Returns one single element of list
|
r"""Returns one single element of list
|
||||||
|
|
||||||
@ -297,9 +392,13 @@ class QueryableABC(ABC):
|
|||||||
ArgumentNoneException: when argument is None
|
ArgumentNoneException: when argument is None
|
||||||
Exception: when argument is None or found more than one element
|
Exception: when argument is None or found more than one element
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) > 1:
|
||||||
|
raise Exception('Found more than one element')
|
||||||
|
elif len(self) == 0:
|
||||||
|
raise Exception('Found no element')
|
||||||
|
|
||||||
|
return self[0]
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def single_or_default(self) -> Optional[any]:
|
def single_or_default(self) -> Optional[any]:
|
||||||
r"""Returns one single element of list
|
r"""Returns one single element of list
|
||||||
|
|
||||||
@ -307,39 +406,48 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Found value: Optional[any]
|
Found value: Optional[any]
|
||||||
"""
|
"""
|
||||||
pass
|
if len(self) > 1:
|
||||||
|
raise Exception('Index out of range')
|
||||||
|
elif len(self) == 0:
|
||||||
|
return None
|
||||||
|
|
||||||
@abstractmethod
|
return self[0]
|
||||||
def skip(self, index: int) -> 'QueryableABC':
|
|
||||||
|
def skip(self, _index: int) -> 'QueryableABC':
|
||||||
r"""Skips all elements from index
|
r"""Skips all elements from index
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _index is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.index)
|
||||||
|
|
||||||
@abstractmethod
|
return type(self)(self.type, values=self[_index:])
|
||||||
def skip_last(self, index: int) -> 'QueryableABC':
|
|
||||||
|
def skip_last(self, _index: int) -> 'QueryableABC':
|
||||||
r"""Skips all elements after index
|
r"""Skips all elements after index
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _index is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.index)
|
||||||
|
|
||||||
|
index = len(self) - _index
|
||||||
|
return type(self)(self._type, self[:index])
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def sum(self, _func: Callable = None) -> Union[int, float, complex]:
|
def sum(self, _func: Callable = None) -> Union[int, float, complex]:
|
||||||
r"""Sum of all values
|
r"""Sum of all values
|
||||||
|
|
||||||
@ -352,39 +460,54 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
Union[int, float, complex]
|
Union[int, float, complex]
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None and not is_number(self.type):
|
||||||
|
raise InvalidTypeException()
|
||||||
|
|
||||||
@abstractmethod
|
if _func is None:
|
||||||
def take(self, index: int) -> 'QueryableABC':
|
_func = _default_lambda
|
||||||
|
|
||||||
|
result = 0
|
||||||
|
for x in self:
|
||||||
|
result += _func(x)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def take(self, _index: int) -> 'QueryableABC':
|
||||||
r"""Takes all elements from index
|
r"""Takes all elements from index
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _index is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.index)
|
||||||
|
|
||||||
@abstractmethod
|
return type(self)(self._type, self[:_index])
|
||||||
def take_last(self, index: int) -> 'QueryableABC':
|
|
||||||
|
def take_last(self, _index: int) -> 'QueryableABC':
|
||||||
r"""Takes all elements after index
|
r"""Takes all elements after index
|
||||||
|
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
index: :class:`int`
|
_index: :class:`int`
|
||||||
index
|
index
|
||||||
|
|
||||||
Returns
|
Returns
|
||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
index = len(self) - _index
|
||||||
|
|
||||||
|
if index >= len(self) or index < 0:
|
||||||
|
raise IndexOutOfRangeException()
|
||||||
|
|
||||||
|
return type(self)(self._type, self[index:])
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def where(self, _func: Callable = None) -> 'QueryableABC':
|
def where(self, _func: Callable = None) -> 'QueryableABC':
|
||||||
r"""Select element by function
|
r"""Select element by function
|
||||||
|
|
||||||
@ -397,4 +520,15 @@ class QueryableABC(ABC):
|
|||||||
-------
|
-------
|
||||||
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
:class: `cpl_query.base.queryable_abc.QueryableABC`
|
||||||
"""
|
"""
|
||||||
pass
|
if _func is None:
|
||||||
|
raise ArgumentNoneException(ExceptionArgument.func)
|
||||||
|
|
||||||
|
if _func is None:
|
||||||
|
_func = _default_lambda
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for element in self:
|
||||||
|
if _func(element):
|
||||||
|
result.append(element)
|
||||||
|
|
||||||
|
return type(self)(self.type, result)
|
||||||
|
86
src/cpl_query/base/sequence_abc.py
Normal file
86
src/cpl_query/base/sequence_abc.py
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
from abc import ABC, abstractmethod
|
||||||
|
from itertools import islice
|
||||||
|
|
||||||
|
from cpl_query.base.sequence_values import SequenceValues
|
||||||
|
|
||||||
|
|
||||||
|
class SequenceABC(ABC):
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def __init__(self, t: type = None, values: list = None):
|
||||||
|
ABC.__init__(self)
|
||||||
|
if values is None:
|
||||||
|
values = []
|
||||||
|
|
||||||
|
if t is None and len(values) > 0:
|
||||||
|
t = type(values[0])
|
||||||
|
|
||||||
|
if t is None:
|
||||||
|
t = any
|
||||||
|
|
||||||
|
self._type = t
|
||||||
|
self._set_values(values)
|
||||||
|
|
||||||
|
def __len__(self):
|
||||||
|
return len(self._values)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._values)
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
return next(self._values)
|
||||||
|
|
||||||
|
def __next__(self):
|
||||||
|
return self.next()
|
||||||
|
|
||||||
|
def __getitem__(self, n):
|
||||||
|
values = [x for x in self]
|
||||||
|
if isinstance(n, slice):
|
||||||
|
try:
|
||||||
|
return values[n]
|
||||||
|
except Exception as e:
|
||||||
|
raise e
|
||||||
|
|
||||||
|
for i in range(len(values)):
|
||||||
|
if i == n:
|
||||||
|
return values[i]
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<{type(self).__name__} {list(self).__repr__()}>'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self) -> type:
|
||||||
|
return self._type
|
||||||
|
|
||||||
|
def _check_type(self, __object: any):
|
||||||
|
if self._type == any:
|
||||||
|
return
|
||||||
|
|
||||||
|
if self._type is not None and type(__object) != self._type and not isinstance(type(__object), self._type) and not issubclass(type(__object), self._type):
|
||||||
|
raise Exception(f'Unexpected type: {type(__object)}\nExpected type: {self._type}')
|
||||||
|
|
||||||
|
def _set_values(self, values: list):
|
||||||
|
self._values = SequenceValues(values, self._type)
|
||||||
|
|
||||||
|
def to_list(self) -> list:
|
||||||
|
r"""Converts :class: `cpl_query.base.sequence_abc.SequenceABC` to :class: `list`
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
:class: `list`
|
||||||
|
"""
|
||||||
|
return [x for x in self]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def empty(cls) -> 'SequenceABC':
|
||||||
|
r"""Returns an empty sequence
|
||||||
|
|
||||||
|
Returns
|
||||||
|
-------
|
||||||
|
Sequence object that contains no elements
|
||||||
|
"""
|
||||||
|
return cls()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def range(cls, start: int, length: int) -> 'SequenceABC':
|
||||||
|
return cls(int, list(range(start, length)))
|
@ -5,15 +5,13 @@ from cpl_query.exceptions import IndexOutOfRangeException
|
|||||||
|
|
||||||
|
|
||||||
class SequenceValues:
|
class SequenceValues:
|
||||||
def __init__(self, data, _t: type):
|
def __init__(self, data: list, _t: type):
|
||||||
if data is None:
|
|
||||||
data = []
|
|
||||||
|
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
def type_check(_t: type, _l: list):
|
def type_check(_t: type, _l: list):
|
||||||
return all(isinstance(x, _t) for x in _l)
|
return all([_t == any or isinstance(x, _t) for x in _l])
|
||||||
|
|
||||||
if not type_check(_t, data):
|
if not type_check(_t, data):
|
||||||
|
print([type(x) for x in data])
|
||||||
raise Exception(f'Unexpected type\nExpected type: {_t}')
|
raise Exception(f'Unexpected type\nExpected type: {_t}')
|
||||||
|
|
||||||
if not hasattr(data, '__iter__'):
|
if not hasattr(data, '__iter__'):
|
||||||
@ -30,7 +28,7 @@ class SequenceValues:
|
|||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
i = 0
|
i = 0
|
||||||
while i < len(self):
|
while i < self._len():
|
||||||
yield next(self._cycle)
|
yield next(self._cycle)
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
|
@ -19,12 +19,9 @@ __version__ = '2022.10.0.post2'
|
|||||||
|
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
# imports:
|
# imports:
|
||||||
from .enumerable import Enumerable
|
from .enumerable import Enumerable
|
||||||
from .enumerable_abc import EnumerableABC
|
from .enumerable_abc import EnumerableABC
|
||||||
from .ordered_enumerable import OrderedEnumerable
|
|
||||||
from .ordered_enumerable_abc import OrderedEnumerableABC
|
|
||||||
|
|
||||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
||||||
version_info = VersionInfo(major='2022', minor='10', micro='0.post2')
|
version_info = VersionInfo(major='2022', minor='10', micro='0.post2')
|
||||||
|
@ -1,9 +1,4 @@
|
|||||||
from typing import Union, Callable, Optional
|
|
||||||
|
|
||||||
from cpl_query._helper import is_number
|
|
||||||
from cpl_query.enumerable.enumerable_abc import EnumerableABC
|
from cpl_query.enumerable.enumerable_abc import EnumerableABC
|
||||||
from cpl_query.enumerable.ordered_enumerable_abc import OrderedEnumerableABC
|
|
||||||
from cpl_query.exceptions import ArgumentNoneException, ExceptionArgument, InvalidTypeException, IndexOutOfRangeException
|
|
||||||
|
|
||||||
|
|
||||||
def _default_lambda(x: object):
|
def _default_lambda(x: object):
|
||||||
@ -14,294 +9,5 @@ class Enumerable(EnumerableABC):
|
|||||||
r"""Implementation of :class: `cpl_query.enumerable.enumerable_abc.EnumerableABC`
|
r"""Implementation of :class: `cpl_query.enumerable.enumerable_abc.EnumerableABC`
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, t: type = None, values: Union[list, iter] = None):
|
def __init__(self, t: type = None, values: list = None):
|
||||||
EnumerableABC.__init__(self, t, values)
|
EnumerableABC.__init__(self, t, values)
|
||||||
|
|
||||||
def all(self, _func: Callable = None) -> bool:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = self.where(_func)
|
|
||||||
return len(result) == len(self)
|
|
||||||
|
|
||||||
def any(self, _func: Callable = None) -> bool:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = self.where(_func)
|
|
||||||
return len(result) > 0
|
|
||||||
|
|
||||||
def average(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return float(self.sum(_func)) / float(self.count())
|
|
||||||
|
|
||||||
def contains(self, _value: object) -> bool:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _value is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.value)
|
|
||||||
|
|
||||||
return self.where(lambda x: x == _value).count() > 0
|
|
||||||
|
|
||||||
def count(self, _func: Callable = None) -> int:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
return len(self)
|
|
||||||
|
|
||||||
return len(self.where(_func))
|
|
||||||
|
|
||||||
def distinct(self, _func: Callable = None) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = Enumerable()
|
|
||||||
known_values = []
|
|
||||||
for element in self:
|
|
||||||
value = _func(element)
|
|
||||||
if value in known_values:
|
|
||||||
continue
|
|
||||||
|
|
||||||
known_values.append(value)
|
|
||||||
result.add(element)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def element_at(self, _index: int) -> any:
|
|
||||||
self._values.reset()
|
|
||||||
while _index >= 0:
|
|
||||||
current = self.next()
|
|
||||||
if _index == 0:
|
|
||||||
return current
|
|
||||||
_index -= 1
|
|
||||||
|
|
||||||
def element_at_or_default(self, _index: int) -> any:
|
|
||||||
try:
|
|
||||||
return self.element_at(_index)
|
|
||||||
except IndexOutOfRangeException:
|
|
||||||
return None
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def empty() -> 'EnumerableABC':
|
|
||||||
r"""Returns an empty enumerable
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
Enumerable object that contains no elements
|
|
||||||
"""
|
|
||||||
return Enumerable()
|
|
||||||
|
|
||||||
def first(self: EnumerableABC, _func=None) -> any:
|
|
||||||
if _func is not None:
|
|
||||||
return self.where(_func).element_at(0)
|
|
||||||
return self.element_at(0)
|
|
||||||
|
|
||||||
def first_or_default(self: EnumerableABC, _func=None) -> Optional[any]:
|
|
||||||
if _func is not None:
|
|
||||||
return self.where(_func).element_at_or_default(0)
|
|
||||||
return self.element_at_or_default(0)
|
|
||||||
|
|
||||||
def for_each(self, _func: Callable = None):
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
for element in self:
|
|
||||||
_func(element)
|
|
||||||
|
|
||||||
def last(self: EnumerableABC) -> any:
|
|
||||||
return self.element_at(self.count() - 1)
|
|
||||||
|
|
||||||
def last_or_default(self: EnumerableABC) -> Optional[any]:
|
|
||||||
return self.element_at_or_default(self.count() - 1)
|
|
||||||
|
|
||||||
def max(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return _func(max(self, key=_func))
|
|
||||||
|
|
||||||
def median(self, _func=None) -> Union[int, float]:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
result = self.order_by(_func).select(_func).to_list()
|
|
||||||
length = len(result)
|
|
||||||
i = int(length / 2)
|
|
||||||
return (
|
|
||||||
result[i]
|
|
||||||
if length % 2 == 1
|
|
||||||
else (float(result[i - 1]) + float(result[i])) / float(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
def min(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return _func(min(self, key=_func))
|
|
||||||
|
|
||||||
def order_by(self, _func: Callable = None) -> OrderedEnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
from cpl_query.enumerable.ordered_enumerable import OrderedEnumerable
|
|
||||||
return OrderedEnumerable(self.type, _func, sorted(self, key=_func))
|
|
||||||
|
|
||||||
def order_by_descending(self, _func: Callable = None) -> OrderedEnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
from cpl_query.enumerable.ordered_enumerable import OrderedEnumerable
|
|
||||||
return OrderedEnumerable(self.type, _func, sorted(self, key=_func, reverse=True))
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def range(start: int, length: int) -> 'EnumerableABC':
|
|
||||||
return Enumerable(int, range(start, length))
|
|
||||||
|
|
||||||
def reverse(self: EnumerableABC) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
return Enumerable(self.type, list(reversed(self.to_list())))
|
|
||||||
|
|
||||||
def select(self, _func: Callable = None) -> EnumerableABC:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
_l = [_func(_o) for _o in self]
|
|
||||||
return Enumerable(self._type if len(_l) < 1 else type(_l[0]), _l)
|
|
||||||
|
|
||||||
def select_many(self, _func: Callable = None) -> EnumerableABC:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
# The line below is pain. I don't understand anything of the list comprehension...
|
|
||||||
# written on 09.11.2022 by Sven Heidemann
|
|
||||||
_l = [_a for _o in self for _a in _func(_o)]
|
|
||||||
return Enumerable(self._type if len(_l) < 1 else type(_l[0]), _l)
|
|
||||||
|
|
||||||
def single(self: EnumerableABC) -> any:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if len(self) > 1:
|
|
||||||
raise IndexError('Found more than one element')
|
|
||||||
elif len(self) == 0:
|
|
||||||
raise IndexOutOfRangeException(f'{type(self).__name__} is empty')
|
|
||||||
|
|
||||||
return self.element_at(0)
|
|
||||||
|
|
||||||
def single_or_default(self: EnumerableABC) -> Optional[any]:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if len(self) > 1:
|
|
||||||
raise IndexError('Found more than one element')
|
|
||||||
elif len(self) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self.element_at(0)
|
|
||||||
|
|
||||||
def skip(self, _index: int) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
_list = self.to_list()
|
|
||||||
|
|
||||||
return Enumerable(self.type, _list[_index:])
|
|
||||||
|
|
||||||
def skip_last(self, _index: int) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
index = len(self) - _index
|
|
||||||
|
|
||||||
return self.take(len(self) - _index)
|
|
||||||
|
|
||||||
def take(self, _index: int) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
_list = self.to_list()
|
|
||||||
|
|
||||||
return Enumerable(self.type, _list[:_index])
|
|
||||||
|
|
||||||
def take_last(self, _index: int) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
_list = self.to_list()
|
|
||||||
index = len(_list) - _index
|
|
||||||
|
|
||||||
return self.skip(index)
|
|
||||||
|
|
||||||
def sum(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return sum([_func(x) for x in self])
|
|
||||||
|
|
||||||
def where(self, _func: Callable = None) -> EnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
return Enumerable(self.type, list(filter(_func, self._values)))
|
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
from abc import abstractmethod
|
from abc import abstractmethod
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from cpl_query.base.queryable_abc import QueryableABC
|
from cpl_query.base.queryable_abc import QueryableABC
|
||||||
from cpl_query.base.sequence_values import SequenceValues
|
|
||||||
|
|
||||||
|
|
||||||
class EnumerableABC(QueryableABC):
|
class EnumerableABC(QueryableABC):
|
||||||
@ -11,81 +9,15 @@ class EnumerableABC(QueryableABC):
|
|||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, t: type = None, values: list = None):
|
def __init__(self, t: type = None, values: list = None):
|
||||||
if t == any or t is None and values is not None:
|
QueryableABC.__init__(self, t, values)
|
||||||
t = type(values[0])
|
|
||||||
|
|
||||||
self._type, self._values, self._remove_error_check = t, SequenceValues(values, t), True
|
self._remove_error_check = True
|
||||||
|
|
||||||
def __len__(self):
|
|
||||||
return len(self._values)
|
|
||||||
|
|
||||||
def __iter__(self):
|
|
||||||
return iter(self._values)
|
|
||||||
|
|
||||||
def next(self):
|
|
||||||
return next(self._values)
|
|
||||||
|
|
||||||
def __next__(self):
|
|
||||||
return self.next()
|
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f'<{type(self).__name__} {list(self).__repr__()}>'
|
|
||||||
|
|
||||||
@property
|
|
||||||
def type(self) -> type:
|
|
||||||
return self._type
|
|
||||||
|
|
||||||
def set_remove_error_check(self, _value: bool):
|
def set_remove_error_check(self, _value: bool):
|
||||||
r"""Set flag to check if element exists before removing
|
r"""Set flag to check if element exists before removing
|
||||||
"""
|
"""
|
||||||
self._remove_error_check = _value
|
self._remove_error_check = _value
|
||||||
|
|
||||||
def add(self, __object: object) -> None:
|
|
||||||
r"""Adds an element to the enumerable.
|
|
||||||
"""
|
|
||||||
if self._type is not None and type(__object) != self._type and not isinstance(type(__object), self._type) and not issubclass(type(__object), self._type):
|
|
||||||
raise Exception(f'Unexpected type: {type(__object)}\nExpected type: {self._type}')
|
|
||||||
|
|
||||||
if len(self) == 0 and self._type is None:
|
|
||||||
self._type = type(__object)
|
|
||||||
|
|
||||||
self._values = SequenceValues([*self._values, __object], self._type)
|
|
||||||
|
|
||||||
def clear(self):
|
|
||||||
r"""Removes all elements
|
|
||||||
"""
|
|
||||||
del self._values
|
|
||||||
self._values = []
|
|
||||||
|
|
||||||
def extend(self, __list: Iterable) -> 'EnumerableABC':
|
|
||||||
r"""Adds elements of given list to enumerable
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
__enumerable: :class: `cpl_query.enumerable.enumerable_abc.EnumerableABC`
|
|
||||||
index
|
|
||||||
"""
|
|
||||||
self._values = SequenceValues([*self._values, *__list], self._type)
|
|
||||||
return self
|
|
||||||
|
|
||||||
def remove(self, __object: object) -> None:
|
|
||||||
r"""Removes element from list
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
__object: :class:`object`
|
|
||||||
value
|
|
||||||
|
|
||||||
Raises
|
|
||||||
---------
|
|
||||||
`Element not found` when element does not exist. Check can be deactivated by calling <enumerable>.set_remove_error_check(False)
|
|
||||||
"""
|
|
||||||
if self._remove_error_check and __object not in self._values:
|
|
||||||
raise Exception('Element not found')
|
|
||||||
|
|
||||||
# self._values.remove(__object)
|
|
||||||
self._values = SequenceValues([x for x in self.to_list() if x != __object], self._type)
|
|
||||||
|
|
||||||
def to_iterable(self) -> 'IterableABC':
|
def to_iterable(self) -> 'IterableABC':
|
||||||
r"""Converts :class: `cpl_query.enumerable.enumerable_abc.EnumerableABC` to :class: `cpl_query.iterable.iterable_abc.IterableABC`
|
r"""Converts :class: `cpl_query.enumerable.enumerable_abc.EnumerableABC` to :class: `cpl_query.iterable.iterable_abc.IterableABC`
|
||||||
|
|
||||||
@ -95,12 +27,3 @@ class EnumerableABC(QueryableABC):
|
|||||||
"""
|
"""
|
||||||
from cpl_query.iterable.iterable import Iterable
|
from cpl_query.iterable.iterable import Iterable
|
||||||
return Iterable(self._type, self.to_list())
|
return Iterable(self._type, self.to_list())
|
||||||
|
|
||||||
def to_list(self) -> list:
|
|
||||||
r"""Converts :class: `cpl_query.base.sequence_abc.SequenceABC` to :class: `list`
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
:class: `list`
|
|
||||||
"""
|
|
||||||
return [x for x in self]
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
from collections.abc import Callable
|
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from cpl_query.enumerable.enumerable import Enumerable
|
|
||||||
from cpl_query.enumerable.ordered_enumerable_abc import OrderedEnumerableABC
|
|
||||||
from cpl_query.exceptions import ArgumentNoneException, ExceptionArgument
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedEnumerable(Enumerable, OrderedEnumerableABC):
|
|
||||||
r"""Implementation of :class: `cpl_query.extension.Enumerable` `cpl_query.extension.OrderedEnumerableABC`
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, _t: type, _func: Callable = None, _values: Iterable = None):
|
|
||||||
Enumerable.__init__(self, _t)
|
|
||||||
OrderedEnumerableABC.__init__(self, _t, _func, _values)
|
|
||||||
|
|
||||||
def then_by(self: OrderedEnumerableABC, _func: Callable) -> OrderedEnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
self._funcs.append(_func)
|
|
||||||
|
|
||||||
return OrderedEnumerable(self.type, _func, sorted(self, key=lambda *args: [f(*args) for f in self._funcs]))
|
|
||||||
|
|
||||||
def then_by_descending(self: OrderedEnumerableABC, _func: Callable) -> OrderedEnumerableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
self._funcs.append(_func)
|
|
||||||
return OrderedEnumerable(self.type, _func, sorted(self, key=lambda *args: [f(*args) for f in self._funcs], reverse=True))
|
|
@ -1,43 +0,0 @@
|
|||||||
from abc import abstractmethod
|
|
||||||
from collections.abc import Callable
|
|
||||||
from typing import Iterable
|
|
||||||
|
|
||||||
from cpl_query.enumerable.enumerable_abc import EnumerableABC
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedEnumerableABC(EnumerableABC):
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def __init__(self, _t: type, _func: Callable = None, _values: Iterable = None):
|
|
||||||
EnumerableABC.__init__(self, _t, _values)
|
|
||||||
self._funcs: list[Callable] = []
|
|
||||||
if _func is not None:
|
|
||||||
self._funcs.append(_func)
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def then_by(self, func: Callable) -> 'OrderedEnumerableABC':
|
|
||||||
r"""Sorts OrderedList in ascending order by function
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
func: :class:`Callable`
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
list of :class:`cpl_query.extension.OrderedEnumerableABC`
|
|
||||||
"""
|
|
||||||
pass
|
|
||||||
|
|
||||||
@abstractmethod
|
|
||||||
def then_by_descending(self, func: Callable) -> 'OrderedEnumerableABC':
|
|
||||||
r"""Sorts OrderedList in descending order by function
|
|
||||||
|
|
||||||
Parameter
|
|
||||||
---------
|
|
||||||
func: :class:`Callable`
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
list of :class:`cpl_query.extension.OrderedEnumerableABC`
|
|
||||||
"""
|
|
||||||
pass
|
|
@ -23,8 +23,6 @@ from collections import namedtuple
|
|||||||
# imports:
|
# imports:
|
||||||
from .iterable_abc import IterableABC
|
from .iterable_abc import IterableABC
|
||||||
from .iterable import Iterable
|
from .iterable import Iterable
|
||||||
from .ordered_iterable_abc import OrderedIterableABC
|
|
||||||
from .ordered_iterable import OrderedIterable
|
|
||||||
|
|
||||||
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
VersionInfo = namedtuple('VersionInfo', 'major minor micro')
|
||||||
version_info = VersionInfo(major='2022', minor='10', micro='0.post2')
|
version_info = VersionInfo(major='2022', minor='10', micro='0.post2')
|
||||||
|
@ -1,9 +1,6 @@
|
|||||||
from typing import Callable, Optional, Union, Iterable as IterableType
|
from typing import Iterable as IterableType
|
||||||
|
|
||||||
from cpl_query._helper import is_number
|
|
||||||
from cpl_query.exceptions import ArgumentNoneException, ExceptionArgument, InvalidTypeException, IndexOutOfRangeException
|
|
||||||
from cpl_query.iterable.iterable_abc import IterableABC
|
from cpl_query.iterable.iterable_abc import IterableABC
|
||||||
from cpl_query.iterable.ordered_iterable_abc import OrderedIterableABC
|
|
||||||
|
|
||||||
|
|
||||||
def _default_lambda(x: object):
|
def _default_lambda(x: object):
|
||||||
@ -14,251 +11,3 @@ class Iterable(IterableABC):
|
|||||||
|
|
||||||
def __init__(self, t: type = None, values: IterableType = None):
|
def __init__(self, t: type = None, values: IterableType = None):
|
||||||
IterableABC.__init__(self, t, values)
|
IterableABC.__init__(self, t, values)
|
||||||
|
|
||||||
def all(self, _func: Callable = None) -> bool:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return self.where(_func).count() == self.count()
|
|
||||||
|
|
||||||
def any(self, _func: Callable = None) -> bool:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return self.where(_func).count() > 0
|
|
||||||
|
|
||||||
def average(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
return self.sum(_func) / self.count()
|
|
||||||
|
|
||||||
def contains(self, _value: object) -> bool:
|
|
||||||
if _value is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.value)
|
|
||||||
|
|
||||||
return self.where(lambda x: x == _value).count() > 0
|
|
||||||
|
|
||||||
def count(self, _func: Callable = None) -> int:
|
|
||||||
if _func is None:
|
|
||||||
return self.__len__()
|
|
||||||
|
|
||||||
return self.where(_func).__len__()
|
|
||||||
|
|
||||||
def distinct(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = Iterable()
|
|
||||||
known_values = []
|
|
||||||
for element in self:
|
|
||||||
value = _func(element)
|
|
||||||
if value in known_values:
|
|
||||||
continue
|
|
||||||
|
|
||||||
known_values.append(value)
|
|
||||||
result.append(element)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def element_at(self, _index: int) -> any:
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
return self[_index]
|
|
||||||
|
|
||||||
def element_at_or_default(self, _index: int) -> any:
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
try:
|
|
||||||
return self[_index]
|
|
||||||
except IndexError:
|
|
||||||
return None
|
|
||||||
|
|
||||||
def first(self) -> any:
|
|
||||||
if len(self) == 0:
|
|
||||||
raise IndexOutOfRangeException()
|
|
||||||
|
|
||||||
return self[0]
|
|
||||||
|
|
||||||
def first_or_default(self) -> Optional[any]:
|
|
||||||
if len(self) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self[0]
|
|
||||||
|
|
||||||
def group_by(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
groups = {}
|
|
||||||
|
|
||||||
for v in self:
|
|
||||||
value = _func(v)
|
|
||||||
if v not in groups:
|
|
||||||
groups[value] = Iterable(type(v))
|
|
||||||
|
|
||||||
groups[value].append(v)
|
|
||||||
|
|
||||||
return Iterable(Iterable).extend(groups.values())
|
|
||||||
|
|
||||||
def last(self) -> any:
|
|
||||||
if len(self) == 0:
|
|
||||||
raise IndexOutOfRangeException()
|
|
||||||
|
|
||||||
return self[len(self) - 1]
|
|
||||||
|
|
||||||
def last_or_default(self) -> Optional[any]:
|
|
||||||
if len(self) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self[len(self) - 1]
|
|
||||||
|
|
||||||
def for_each(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
if _func is not None:
|
|
||||||
for element in self:
|
|
||||||
_func(element)
|
|
||||||
|
|
||||||
return self
|
|
||||||
|
|
||||||
def max(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return _func(max(self, key=_func))
|
|
||||||
|
|
||||||
def median(self, _func=None) -> Union[int, float]:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = self.order_by(_func).select(_func).to_list()
|
|
||||||
length = len(result)
|
|
||||||
i = int(length / 2)
|
|
||||||
return (
|
|
||||||
result[i]
|
|
||||||
if length % 2 == 1
|
|
||||||
else (float(result[i - 1]) + float(result[i])) / float(2)
|
|
||||||
)
|
|
||||||
|
|
||||||
def min(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
return _func(min(self, key=_func))
|
|
||||||
|
|
||||||
def order_by(self, _func: Callable = None) -> OrderedIterableABC:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
from cpl_query.iterable.ordered_iterable import OrderedIterable
|
|
||||||
return OrderedIterable(self.type, _func, sorted(self, key=_func))
|
|
||||||
|
|
||||||
def order_by_descending(self, _func: Callable = None) -> OrderedIterableABC:
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
from cpl_query.iterable.ordered_iterable import OrderedIterable
|
|
||||||
return OrderedIterable(self.type, _func, sorted(self, key=_func, reverse=True))
|
|
||||||
|
|
||||||
def reverse(self) -> 'Iterable':
|
|
||||||
return Iterable().extend(reversed(self))
|
|
||||||
|
|
||||||
def select(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = Iterable()
|
|
||||||
result.extend(_func(_o) for _o in self)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def select_many(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
result = Iterable()
|
|
||||||
# The line below is pain. I don't understand anything of it...
|
|
||||||
# written on 09.11.2022 by Sven Heidemann
|
|
||||||
elements = [_a for _o in self for _a in _func(_o)]
|
|
||||||
|
|
||||||
result.extend(elements)
|
|
||||||
return result
|
|
||||||
|
|
||||||
def single(self) -> any:
|
|
||||||
if len(self) > 1:
|
|
||||||
raise Exception('Found more than one element')
|
|
||||||
elif len(self) == 0:
|
|
||||||
raise Exception('Found no element')
|
|
||||||
|
|
||||||
return self[0]
|
|
||||||
|
|
||||||
def single_or_default(self) -> Optional[any]:
|
|
||||||
if len(self) > 1:
|
|
||||||
raise Exception('Index out of range')
|
|
||||||
elif len(self) == 0:
|
|
||||||
return None
|
|
||||||
|
|
||||||
return self[0]
|
|
||||||
|
|
||||||
def skip(self, _index: int) -> 'Iterable':
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
return Iterable(self.type, values=self[_index:])
|
|
||||||
|
|
||||||
def skip_last(self, _index: int) -> 'Iterable':
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
index = len(self) - _index
|
|
||||||
|
|
||||||
result = Iterable()
|
|
||||||
result.extend(self[:index])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def sum(self, _func: Callable = None) -> Union[int, float, complex]:
|
|
||||||
if _func is None and not is_number(self.type):
|
|
||||||
raise InvalidTypeException()
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = 0
|
|
||||||
for x in self:
|
|
||||||
result += _func(x)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def take(self, _index: int) -> 'Iterable':
|
|
||||||
if _index is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.index)
|
|
||||||
|
|
||||||
result = Iterable()
|
|
||||||
result.extend(self[:_index])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def take_last(self, _index: int) -> 'Iterable':
|
|
||||||
index = len(self) - _index
|
|
||||||
|
|
||||||
if index >= len(self) or index < 0:
|
|
||||||
raise IndexOutOfRangeException()
|
|
||||||
|
|
||||||
result = Iterable()
|
|
||||||
result.extend(self[index:])
|
|
||||||
return result
|
|
||||||
|
|
||||||
def where(self, _func: Callable = None) -> 'Iterable':
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
_func = _default_lambda
|
|
||||||
|
|
||||||
result = Iterable(self.type)
|
|
||||||
for element in self:
|
|
||||||
if _func(element):
|
|
||||||
result.append(element)
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
@ -4,54 +4,45 @@ from typing import Iterable
|
|||||||
from cpl_query.base.queryable_abc import QueryableABC
|
from cpl_query.base.queryable_abc import QueryableABC
|
||||||
|
|
||||||
|
|
||||||
class IterableABC(list, QueryableABC):
|
class IterableABC(QueryableABC):
|
||||||
r"""ABC to define functions on list
|
r"""ABC to define functions on list
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def __init__(self, t: type = None, values: Iterable = None):
|
def __init__(self, t: type = None, values: Iterable = None):
|
||||||
values = [] if values is None else values
|
QueryableABC.__init__(self, t, values)
|
||||||
list.__init__(self, values)
|
|
||||||
|
|
||||||
if t is None and len(values) > 0:
|
def __setitem__(self, i, val):
|
||||||
t = type(values[0])
|
self._check_type(val)
|
||||||
|
values = [*self._values]
|
||||||
|
values[i] = val
|
||||||
|
self._set_values(values)
|
||||||
|
|
||||||
self._type = t
|
def __delitem__(self, i):
|
||||||
|
values = [*self._values]
|
||||||
def __repr__(self):
|
del values[i]
|
||||||
return f'<{type(self).__name__} {list(self).__repr__()}>'
|
self._set_values(values)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def type(self) -> type:
|
def type(self) -> type:
|
||||||
return self._type
|
return self._type
|
||||||
|
|
||||||
def to_list(self) -> list:
|
|
||||||
r"""Converts :class: `cpl_query.base.sequence_abc.SequenceABC` to :class: `list`
|
|
||||||
|
|
||||||
Returns
|
|
||||||
-------
|
|
||||||
:class: `list`
|
|
||||||
"""
|
|
||||||
return [x for x in self]
|
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.to_list())
|
return str(self.to_list())
|
||||||
|
|
||||||
def append(self, __object: object) -> None:
|
def append(self, _object: object):
|
||||||
|
self.add(_object)
|
||||||
|
|
||||||
|
def add(self, _object: object):
|
||||||
r"""Adds element to list
|
r"""Adds element to list
|
||||||
Parameter
|
Parameter
|
||||||
---------
|
---------
|
||||||
__object: :class:`object`
|
_object: :class:`object`
|
||||||
value
|
value
|
||||||
"""
|
"""
|
||||||
if self._type is not None and type(__object) != self._type and not isinstance(type(__object), self._type) and not issubclass(type(__object), self._type):
|
self._check_type(_object)
|
||||||
raise Exception(f'Unexpected type: {type(__object)}\nExpected type: {self._type}')
|
values = [*self._values, _object]
|
||||||
|
self._set_values(values)
|
||||||
if len(self) == 0 and self._type is None:
|
|
||||||
self._type = type(__object)
|
|
||||||
|
|
||||||
# self._values = SequenceValues([*self._values, __object], self._type)
|
|
||||||
super().append(__object)
|
|
||||||
|
|
||||||
def extend(self, __iterable: Iterable) -> 'IterableABC':
|
def extend(self, __iterable: Iterable) -> 'IterableABC':
|
||||||
r"""Adds elements of given list to list
|
r"""Adds elements of given list to list
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
from collections.abc import Callable
|
|
||||||
|
|
||||||
from cpl_query.exceptions import ArgumentNoneException, ExceptionArgument
|
|
||||||
from cpl_query.iterable.iterable import Iterable
|
|
||||||
from cpl_query.iterable.ordered_iterable_abc import OrderedIterableABC
|
|
||||||
|
|
||||||
|
|
||||||
class OrderedIterable(Iterable, OrderedIterableABC):
|
|
||||||
r"""Implementation of :class: `cpl_query.extension.Iterable` `cpl_query.extension.OrderedIterableABC`
|
|
||||||
"""
|
|
||||||
|
|
||||||
def __init__(self, _t: type, _func: Callable = None, _values: Iterable = None):
|
|
||||||
Iterable.__init__(self, _t)
|
|
||||||
OrderedIterableABC.__init__(self, _t, _func, _values)
|
|
||||||
|
|
||||||
def then_by(self: OrderedIterableABC, _func: Callable) -> OrderedIterableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
self._funcs.append(_func)
|
|
||||||
|
|
||||||
return OrderedIterable(self.type, _func, sorted(self, key=lambda *args: [f(*args) for f in self._funcs]))
|
|
||||||
|
|
||||||
def then_by_descending(self: OrderedIterableABC, _func: Callable) -> OrderedIterableABC:
|
|
||||||
if self is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.list)
|
|
||||||
|
|
||||||
if _func is None:
|
|
||||||
raise ArgumentNoneException(ExceptionArgument.func)
|
|
||||||
|
|
||||||
self._funcs.append(_func)
|
|
||||||
return OrderedIterable(self.type, _func, sorted(self, key=lambda *args: [f(*args) for f in self._funcs], reverse=True))
|
|
@ -6,4 +6,4 @@ if not os.getcwd().endswith('unittests'):
|
|||||||
|
|
||||||
PLAYGROUND_PATH = os.path.abspath(os.path.join(os.getcwd(), f'{base}test_cli_playground'))
|
PLAYGROUND_PATH = os.path.abspath(os.path.join(os.getcwd(), f'{base}test_cli_playground'))
|
||||||
TRANSLATION_PATH = os.path.abspath(os.path.join(os.getcwd(), f'{base}unittests_translation'))
|
TRANSLATION_PATH = os.path.abspath(os.path.join(os.getcwd(), f'{base}unittests_translation'))
|
||||||
CLI_PATH = os.path.abspath(os.path.join(os.getcwd(), f'{base}../src/cpl_cli/main.py'))
|
CLI_PATH = os.path.abspath(os.path.join(os.getcwd(), f'../../src/cpl_cli/main.py'))
|
||||||
|
@ -11,7 +11,16 @@ from unittests_query.models import User, Address
|
|||||||
class EnumerableQueryTestCase(unittest.TestCase):
|
class EnumerableQueryTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self._tests = Enumerable(User)
|
users = []
|
||||||
|
for i in range(0, 100):
|
||||||
|
users.append(User(
|
||||||
|
String.random_string(string.ascii_letters, 8).lower(),
|
||||||
|
Address(
|
||||||
|
String.random_string(string.ascii_letters, 10).lower(),
|
||||||
|
randint(1, 10)
|
||||||
|
)
|
||||||
|
))
|
||||||
|
|
||||||
self._t_user = User(
|
self._t_user = User(
|
||||||
'Test user',
|
'Test user',
|
||||||
Address(
|
Address(
|
||||||
@ -27,22 +36,10 @@ class EnumerableQueryTestCase(unittest.TestCase):
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
self._generate_test_data()
|
users.append(self._t_user)
|
||||||
|
users.append(self._t_user2)
|
||||||
|
|
||||||
def _generate_test_data(self):
|
self._tests = Enumerable(User, users)
|
||||||
for i in range(0, 100):
|
|
||||||
user = User(
|
|
||||||
String.random_string(string.ascii_letters, 8).lower(),
|
|
||||||
Address(
|
|
||||||
String.random_string(string.ascii_letters, 10).lower(),
|
|
||||||
randint(1, 10)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
self._tests.add(user)
|
|
||||||
|
|
||||||
self._tests.add(self._t_user)
|
|
||||||
self._tests.add(self._t_user2)
|
|
||||||
|
|
||||||
def test_any(self):
|
def test_any(self):
|
||||||
results = []
|
results = []
|
||||||
|
@ -6,17 +6,9 @@ from cpl_query.enumerable.enumerable import Enumerable
|
|||||||
class EnumerableTestCase(unittest.TestCase):
|
class EnumerableTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
self._list = Enumerable(int)
|
self._list = Enumerable(int, list(range(1, 4)))
|
||||||
|
|
||||||
def _clear(self):
|
|
||||||
self._list.clear()
|
|
||||||
self.assertEqual(self._list, [])
|
|
||||||
|
|
||||||
def test_append(self):
|
def test_append(self):
|
||||||
self._list.add(1)
|
|
||||||
self._list.add(2)
|
|
||||||
self._list.add(3)
|
|
||||||
|
|
||||||
self.assertEqual(self._list.to_list(), [1, 2, 3])
|
self.assertEqual(self._list.to_list(), [1, 2, 3])
|
||||||
self.assertRaises(Exception, lambda v: self._list.add(v), '3')
|
self.assertRaises(Exception, lambda v: self._list.add(v), '3')
|
||||||
|
|
||||||
@ -38,30 +30,7 @@ class EnumerableTestCase(unittest.TestCase):
|
|||||||
n += 1
|
n += 1
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
self._list.add(1)
|
|
||||||
self._list.add(2)
|
|
||||||
self._list.add(3)
|
|
||||||
|
|
||||||
self.assertEqual(self._list.element_at(2), [1, 2, 3][2])
|
self.assertEqual(self._list.element_at(2), [1, 2, 3][2])
|
||||||
|
|
||||||
def test_count(self):
|
def test_count(self):
|
||||||
self._list.add(1)
|
|
||||||
self._list.add(2)
|
|
||||||
self._list.add(3)
|
|
||||||
|
|
||||||
self.assertEqual(self._list.count(), 3)
|
self.assertEqual(self._list.count(), 3)
|
||||||
|
|
||||||
def test_remove(self):
|
|
||||||
old_values = self._list._values
|
|
||||||
self._list.add(1)
|
|
||||||
self.assertNotEqual(old_values, self._list._values)
|
|
||||||
self._list.add(2)
|
|
||||||
self._list.add(3)
|
|
||||||
|
|
||||||
self.assertEqual(self._list.to_list(), [1, 2, 3])
|
|
||||||
self.assertRaises(Exception, lambda v: self._list.add(v), '3')
|
|
||||||
old_values = self._list._values
|
|
||||||
self._list.remove(3)
|
|
||||||
self.assertNotEqual(old_values, self._list._values)
|
|
||||||
self.assertEqual(self._list.to_list(), [1, 2])
|
|
||||||
self.assertRaises(Exception, lambda v: self._list.add(v), '3')
|
|
||||||
|
@ -113,7 +113,7 @@ class IterableQueryTestCase(unittest.TestCase):
|
|||||||
|
|
||||||
addresses.append(u.address.nr)
|
addresses.append(u.address.nr)
|
||||||
|
|
||||||
res2 = self._tests.distinct(lambda x: x.address.nr).select(lambda x: x.address.nr)
|
res2 = self._tests.distinct(lambda x: x.address.nr).select(lambda x: x.address.nr).to_list()
|
||||||
self.assertEqual(addresses, res2)
|
self.assertEqual(addresses, res2)
|
||||||
|
|
||||||
def test_element_at(self):
|
def test_element_at(self):
|
||||||
@ -199,7 +199,7 @@ class IterableQueryTestCase(unittest.TestCase):
|
|||||||
groups[v].append(v)
|
groups[v].append(v)
|
||||||
elements.append(v)
|
elements.append(v)
|
||||||
|
|
||||||
r1, r2 = list(groups.values()), elements.group_by()
|
r1, r2 = list(groups.values()), elements.group_by().select(lambda l: l.to_list()).to_list()
|
||||||
self.assertEqual(r1, r2)
|
self.assertEqual(r1, r2)
|
||||||
|
|
||||||
def test_for_each(self):
|
def test_for_each(self):
|
||||||
|
@ -4,9 +4,15 @@ class User:
|
|||||||
self.name = name
|
self.name = name
|
||||||
self.address = address
|
self.address = address
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<{type(self).__name__} {self.name} {self.address}>'
|
||||||
|
|
||||||
|
|
||||||
class Address:
|
class Address:
|
||||||
|
|
||||||
def __init__(self, street, nr):
|
def __init__(self, street, nr):
|
||||||
self.street = street
|
self.street = street
|
||||||
self.nr = nr
|
self.nr = nr
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f'<{type(self).__name__} {self.street} {self.nr}>'
|
||||||
|
Loading…
Reference in New Issue
Block a user