generated from daniil-berg/boilerplate-py
67 lines
2.5 KiB
Python
67 lines
2.5 KiB
Python
from operator import attrgetter
|
|
from typing import Any, Callable, TypeVar
|
|
|
|
|
|
T = TypeVar('T')
|
|
KeyFuncT = Callable[[T], Any]
|
|
_sentinel = object()
|
|
|
|
|
|
def multi_sort(obj_list: list[T], *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool]) -> None:
|
|
for param in reversed(parameters):
|
|
if isinstance(param, str):
|
|
obj_list.sort(key=attrgetter(param))
|
|
elif callable(param):
|
|
obj_list.sort(key=param)
|
|
else:
|
|
try:
|
|
param, reverse = param
|
|
assert isinstance(reverse, bool)
|
|
except (ValueError, TypeError):
|
|
raise ValueError(f"Sorting parameter {param} is neither a key nor a key-boolean-tuple.")
|
|
if isinstance(param, str):
|
|
obj_list.sort(key=attrgetter(param), reverse=reverse)
|
|
elif callable(param):
|
|
obj_list.sort(key=param, reverse=reverse)
|
|
else:
|
|
raise ValueError(f"Sorting key {param} is neither a string nor a callable.")
|
|
|
|
|
|
def multi_gt(left: T, right: T, *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool]) -> bool:
|
|
for param in parameters:
|
|
invert = False
|
|
if isinstance(param, str):
|
|
left_val, right_val = getattr(left, param), getattr(right, param)
|
|
elif callable(param):
|
|
left_val, right_val = param(left), param(right)
|
|
else:
|
|
try:
|
|
param, invert = param
|
|
assert isinstance(invert, bool)
|
|
except (ValueError, TypeError, AssertionError):
|
|
raise ValueError(f"Ordering parameter {param} is neither a key nor a key-boolean-tuple.")
|
|
if isinstance(param, str):
|
|
left_val, right_val = getattr(left, param), getattr(right, param)
|
|
elif callable(param):
|
|
left_val, right_val = param(left), param(right)
|
|
else:
|
|
raise ValueError(f"Ordering key {param} is neither a string nor a callable.")
|
|
if left_val == right_val:
|
|
continue
|
|
return left_val < right_val if invert else left_val > right_val
|
|
return False
|
|
|
|
|
|
def multi_max(obj_list: list[T], *parameters: str | KeyFuncT | tuple[str | KeyFuncT, bool],
|
|
default: Any = _sentinel) -> T:
|
|
try:
|
|
largest = obj_list[0]
|
|
except IndexError:
|
|
if default is not _sentinel:
|
|
return default
|
|
raise ValueError("Cannot get largest item from an empty list.")
|
|
for obj in obj_list[1:]:
|
|
if multi_gt(obj, largest, *parameters):
|
|
largest = obj
|
|
return largest
|