anb-python-components 1.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- anb_python_components/__init__.py +1 -0
- anb_python_components/classes/__init__.py +1 -0
- anb_python_components/classes/action_state.py +211 -0
- anb_python_components/classes/directory.py +115 -0
- anb_python_components/classes/file.py +226 -0
- anb_python_components/classes/shortcode_parser.py +195 -0
- anb_python_components/custom_types/__init__.py +3 -0
- anb_python_components/custom_types/guid.py +169 -0
- anb_python_components/custom_types/object_array.py +625 -0
- anb_python_components/custom_types/shortcode_attributes.py +128 -0
- anb_python_components/custom_types/two_dim_size.py +413 -0
- anb_python_components/custom_types/version_info.py +461 -0
- anb_python_components/enums/__init__.py +1 -0
- anb_python_components/enums/message_type.py +44 -0
- anb_python_components/enums/not_bool_action.py +24 -0
- anb_python_components/enums/type_copy_strategy.py +47 -0
- anb_python_components/exceptions/__init__.py +1 -0
- anb_python_components/exceptions/wrong_type_exception.py +20 -0
- anb_python_components/extensions/__init__.py +1 -0
- anb_python_components/extensions/array_extension.py +34 -0
- anb_python_components/extensions/bool_extension.py +87 -0
- anb_python_components/extensions/string_extension.py +259 -0
- anb_python_components/extensions/string_extension_constant.py +80 -0
- anb_python_components/extensions/type_extension.py +112 -0
- anb_python_components/models/__init__.py +1 -0
- anb_python_components/models/action_state_message.py +27 -0
- anb_python_components/models/shortcode_model.py +31 -0
- anb_python_components-1.2.1.dist-info/METADATA +12 -0
- anb_python_components-1.2.1.dist-info/RECORD +48 -0
- anb_python_components-1.2.1.dist-info/WHEEL +5 -0
- anb_python_components-1.2.1.dist-info/licenses/LICENSE +235 -0
- anb_python_components-1.2.1.dist-info/top_level.txt +2 -0
- tests/__init__.py +1 -0
- tests/classes/__init__.py +1 -0
- tests/classes/action_state_test.py +138 -0
- tests/classes/directory_test.py +19 -0
- tests/classes/file_test.py +79 -0
- tests/classes/shortcode_parser_test.py +105 -0
- tests/custom_types/__init__.py +1 -0
- tests/custom_types/guid_test.py +14 -0
- tests/custom_types/object_array_test.py +160 -0
- tests/custom_types/two_dim_size_test.py +37 -0
- tests/custom_types/version_info_test.py +51 -0
- tests/extensions/__init__.py +1 -0
- tests/extensions/array_extension_test.py +21 -0
- tests/extensions/bool_extension_test.py +19 -0
- tests/extensions/string_extension_test.py +55 -0
- tests/extensions/type_extension_test.py +38 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
# anb_python_components/custom_types/object_array.py
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import copy
|
|
5
|
+
from functools import cmp_to_key, reduce
|
|
6
|
+
from typing import Any, Callable, get_args, get_type_hints
|
|
7
|
+
|
|
8
|
+
from anb_python_components.enums.type_copy_strategy import TypeCopyStrategy
|
|
9
|
+
from anb_python_components.extensions.type_extension import TypeExtension
|
|
10
|
+
|
|
11
|
+
class ObjectArray[T]:
|
|
12
|
+
"""
|
|
13
|
+
Класс для хранения массива объектов.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
# Добавление слотов для хранения массива
|
|
17
|
+
__slots__ = ['__container']
|
|
18
|
+
|
|
19
|
+
def __init__ (self, array: list[T] | None = None, copy_strategy: TypeCopyStrategy = TypeCopyStrategy.AUTO):
|
|
20
|
+
"""
|
|
21
|
+
Инициализация массива объектов.
|
|
22
|
+
|
|
23
|
+
:param array: Список объектов или None.
|
|
24
|
+
:type array: list[T] | None
|
|
25
|
+
"""
|
|
26
|
+
# Если массив не задан
|
|
27
|
+
if array is None:
|
|
28
|
+
# - то создаем пустой массив
|
|
29
|
+
self.__container: list[T] = []
|
|
30
|
+
|
|
31
|
+
# Если массив задан, то присваиваем его согласно стратегии копирования
|
|
32
|
+
match copy_strategy:
|
|
33
|
+
# - если копировать не нужно
|
|
34
|
+
case TypeCopyStrategy.IGNORE:
|
|
35
|
+
# -- то просто присваиваем его
|
|
36
|
+
self.__container: list[T] = array
|
|
37
|
+
|
|
38
|
+
# - если это примитивный тип
|
|
39
|
+
case TypeCopyStrategy.COPY:
|
|
40
|
+
# -- то копируем его
|
|
41
|
+
self.__container: list[T] = array.copy()
|
|
42
|
+
# - если это сложный тип
|
|
43
|
+
case TypeCopyStrategy.DEEP_COPY:
|
|
44
|
+
# -- то глубоко копируем его
|
|
45
|
+
self.__container: list[T] = copy.deepcopy(array)
|
|
46
|
+
# - если стратегия - автоматический выбор
|
|
47
|
+
case TypeCopyStrategy.AUTO:
|
|
48
|
+
# -- получаем тип аргумента T
|
|
49
|
+
t_type = get_args(get_type_hints(self.__class__).get('T'))
|
|
50
|
+
|
|
51
|
+
# -- анализируем его
|
|
52
|
+
if TypeExtension.is_immutable_type(t_type):
|
|
53
|
+
# --- если тип T - примитивный, то просто копируем его
|
|
54
|
+
self.__container: list[T] = array.copy()
|
|
55
|
+
else:
|
|
56
|
+
# --- иначе глубоко копируем его
|
|
57
|
+
self.__container: list[T] = copy.deepcopy(array)
|
|
58
|
+
|
|
59
|
+
def __iter__ (self):
|
|
60
|
+
"""
|
|
61
|
+
Итератор.
|
|
62
|
+
:return: Итератор.
|
|
63
|
+
:rtype: Iterator[T]
|
|
64
|
+
"""
|
|
65
|
+
return iter(self.__container)
|
|
66
|
+
|
|
67
|
+
def __getitem__ (self, key: int) -> T | None:
|
|
68
|
+
"""
|
|
69
|
+
Доступ к атрибутам по ключу.
|
|
70
|
+
:param key: Ключ атрибута.
|
|
71
|
+
:type key: int
|
|
72
|
+
:return: Значение атрибута или None, если атрибут не найден.
|
|
73
|
+
:rtype: T | None
|
|
74
|
+
"""
|
|
75
|
+
# Если ключ отрицательный или больше или равен длине массива
|
|
76
|
+
if key < 0 or key >= len(self.__container):
|
|
77
|
+
# - то возвращаем None
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
# Возвращаем значение
|
|
81
|
+
return self.__container[key]
|
|
82
|
+
|
|
83
|
+
def __setitem__ (self, key: int, value: T) -> None:
|
|
84
|
+
"""
|
|
85
|
+
Установить значение атрибута.
|
|
86
|
+
:param key: Ключ атрибута.
|
|
87
|
+
:type key: int
|
|
88
|
+
:param value: Значение атрибута.
|
|
89
|
+
:type value: T
|
|
90
|
+
:return: None
|
|
91
|
+
"""
|
|
92
|
+
self.__container[key] = value
|
|
93
|
+
|
|
94
|
+
def __contains__ (self, item: T) -> bool:
|
|
95
|
+
"""
|
|
96
|
+
Проверка наличия элемента в массиве.
|
|
97
|
+
:param item: Проверяемый элемент.
|
|
98
|
+
:type item: T
|
|
99
|
+
:return: True, если элемент найден, False, если элемент не найден.
|
|
100
|
+
"""
|
|
101
|
+
return self.is_exists(lambda elem: elem == item)
|
|
102
|
+
|
|
103
|
+
def __len__ (self) -> int:
|
|
104
|
+
"""
|
|
105
|
+
Количество атрибутов.
|
|
106
|
+
:return: Количество атрибутов.
|
|
107
|
+
:rtype: int
|
|
108
|
+
"""
|
|
109
|
+
return self.count()
|
|
110
|
+
|
|
111
|
+
@staticmethod
|
|
112
|
+
def default_compare () -> Callable[[T, T], bool]:
|
|
113
|
+
"""
|
|
114
|
+
Статический метод для получения функции сравнения по умолчанию.
|
|
115
|
+
:return: Функция сравнения по умолчанию.
|
|
116
|
+
:rtype: Callable[[T, T], bool]
|
|
117
|
+
"""
|
|
118
|
+
return lambda x, y: x == y
|
|
119
|
+
|
|
120
|
+
### Специальные методы ###
|
|
121
|
+
def clear (self) -> None:
|
|
122
|
+
"""
|
|
123
|
+
Очистка массива.
|
|
124
|
+
"""
|
|
125
|
+
self.__container.clear()
|
|
126
|
+
|
|
127
|
+
def add (self, value: T) -> None:
|
|
128
|
+
"""
|
|
129
|
+
Добавление значения в массив.
|
|
130
|
+
:param value: Значение.
|
|
131
|
+
:type value: T
|
|
132
|
+
"""
|
|
133
|
+
# Если значения нет в массиве
|
|
134
|
+
if value not in self.__container:
|
|
135
|
+
# - то добавляем
|
|
136
|
+
self.__container.append(value)
|
|
137
|
+
|
|
138
|
+
def add_range (self, values: list[T] | ObjectArray[T]) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Добавление диапазона значений в массив.
|
|
141
|
+
:param values: Значения, которые нужно добавить. Можно передавать массив или объект класса ObjectArray.
|
|
142
|
+
:type values: list[T] | ObjectArray[T]
|
|
143
|
+
"""
|
|
144
|
+
# Если передан массив, то не изменяем его, а если передан объект класса ObjectArray, то конвертируем его в массив объектов
|
|
145
|
+
object_array = values.to_array() if isinstance(values, ObjectArray) else values
|
|
146
|
+
|
|
147
|
+
# Если значения есть
|
|
148
|
+
if len(object_array) > 0:
|
|
149
|
+
# - то добавляем их
|
|
150
|
+
self.__container += object_array
|
|
151
|
+
|
|
152
|
+
def to_array (self) -> list[T]:
|
|
153
|
+
"""
|
|
154
|
+
Получение массива.
|
|
155
|
+
:return: Массив.
|
|
156
|
+
:rtype: list[T]
|
|
157
|
+
"""
|
|
158
|
+
# Если массив не пустой
|
|
159
|
+
if len(self.__container) > 0:
|
|
160
|
+
# - то возвращаем его
|
|
161
|
+
return self.__container
|
|
162
|
+
else:
|
|
163
|
+
# - иначе возвращаем пустой массив
|
|
164
|
+
return []
|
|
165
|
+
|
|
166
|
+
### Поиск и сортировка ###
|
|
167
|
+
def find (self, value: Any, compare: Callable[[T, Any], bool] = default_compare()) -> T | None:
|
|
168
|
+
"""
|
|
169
|
+
Поиск значения в массиве.
|
|
170
|
+
:param value: Значение, которое нужно найти.
|
|
171
|
+
:type value: Any
|
|
172
|
+
:param compare: Функция сравнения.
|
|
173
|
+
:type value: Callable[[T, Any], bool]
|
|
174
|
+
:return: Найденное значение или None.
|
|
175
|
+
:rtype: T | None
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
# Для каждого элемента массива
|
|
179
|
+
for item in self.__container:
|
|
180
|
+
# - выполняем сравнение по функции сравнения
|
|
181
|
+
if compare(item, value):
|
|
182
|
+
# -- и возвращаем элемент, если он найден
|
|
183
|
+
return item
|
|
184
|
+
|
|
185
|
+
# Если мы сюда дошли, значить объект не найден - возвращаем None
|
|
186
|
+
return None
|
|
187
|
+
|
|
188
|
+
def sort (self, object_property: str, descending: bool = False) -> None:
|
|
189
|
+
"""
|
|
190
|
+
Сортирует контейнер объектов по указанному атрибуту.
|
|
191
|
+
|
|
192
|
+
:param object_property: Имя атрибута объекта для сортировки
|
|
193
|
+
:type object_property: str
|
|
194
|
+
:param descending: если True — сортировка по убыванию
|
|
195
|
+
:type descending: bool
|
|
196
|
+
"""
|
|
197
|
+
# Копируем список (чтобы не сортировать исходный напрямую)
|
|
198
|
+
result: list[T] = self.__container[:]
|
|
199
|
+
|
|
200
|
+
# Сортируем по указанному атрибуту
|
|
201
|
+
result.sort(
|
|
202
|
+
key = lambda obj: getattr(obj, object_property),
|
|
203
|
+
reverse = descending
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
# Присваиваем результат обратно в контейнер
|
|
207
|
+
self.__container = result
|
|
208
|
+
|
|
209
|
+
def sort_callback (
|
|
210
|
+
self,
|
|
211
|
+
predicate: Callable[[T], Any],
|
|
212
|
+
descending: bool = False
|
|
213
|
+
) -> None:
|
|
214
|
+
"""
|
|
215
|
+
Сортирует контейнер, используя пользовательскую функцию-предикат.
|
|
216
|
+
|
|
217
|
+
:param predicate: Функция, принимающая объект и возвращающая значение свойства для сравнения.
|
|
218
|
+
:type predicate: Callable[[T], Any]
|
|
219
|
+
:param descending: если True — сортировка по убыванию.
|
|
220
|
+
:type descending: bool
|
|
221
|
+
"""
|
|
222
|
+
# Копируем список (чтобы не сортировать исходный напрямую)
|
|
223
|
+
result: list[T] = self.__container[:]
|
|
224
|
+
|
|
225
|
+
# Определяем компаратор
|
|
226
|
+
def comparator (a: Any, b: Any) -> int:
|
|
227
|
+
"""
|
|
228
|
+
Функция сравнения двух значений.
|
|
229
|
+
:param a: Значение 1.
|
|
230
|
+
:type a: Any
|
|
231
|
+
:param b: Значение 2.
|
|
232
|
+
:type b: Any
|
|
233
|
+
:return: -1, если значение 1 меньше значения 2, 1, если значение 1 больше значения 2 и 0, если значения равны.
|
|
234
|
+
:rtype: int
|
|
235
|
+
"""
|
|
236
|
+
# - получаем значения для сравнения по переданной функции
|
|
237
|
+
# -- значение 1
|
|
238
|
+
val_a = predicate(a)
|
|
239
|
+
# -- значение 2
|
|
240
|
+
val_b = predicate(b)
|
|
241
|
+
|
|
242
|
+
# - если значение 1 меньше значения 2
|
|
243
|
+
if val_a < val_b:
|
|
244
|
+
# -- то возвращаем -1
|
|
245
|
+
return -1
|
|
246
|
+
# - если значение 1 больше значения 2
|
|
247
|
+
elif val_a > val_b:
|
|
248
|
+
# -- то возвращаем 1
|
|
249
|
+
return 1
|
|
250
|
+
# - если значения равны
|
|
251
|
+
else:
|
|
252
|
+
# -- то возвращаем 0
|
|
253
|
+
return 0
|
|
254
|
+
|
|
255
|
+
# Если нужен обратный порядок
|
|
256
|
+
if descending:
|
|
257
|
+
# - то создаем компаратор, который меняет порядок
|
|
258
|
+
def reversed_comparator (a: Any, b: Any) -> int:
|
|
259
|
+
"""
|
|
260
|
+
Функция для создания компаратора, который меняет порядок.
|
|
261
|
+
:param a: Значение 1.
|
|
262
|
+
:type a: Any
|
|
263
|
+
:param b: Значение 2.
|
|
264
|
+
:type b: Any
|
|
265
|
+
:return: -1, если значение 1 больше значения 2, 1, если значение 1 меньше значения 2 и 0, если значения равны.
|
|
266
|
+
:rtype: int
|
|
267
|
+
"""
|
|
268
|
+
return -comparator(a, b)
|
|
269
|
+
|
|
270
|
+
# - сортируем по компаратору, который меняет порядок
|
|
271
|
+
result.sort(key = cmp_to_key(reversed_comparator))
|
|
272
|
+
else:
|
|
273
|
+
# - иначе сортируем по компаратору по умолчанию
|
|
274
|
+
result.sort(key = cmp_to_key(comparator))
|
|
275
|
+
|
|
276
|
+
# Присваиваем результат обратно в контейнер
|
|
277
|
+
self.__container = result
|
|
278
|
+
|
|
279
|
+
### Операторы LINQ ###
|
|
280
|
+
### 1. Операторы проверки существования и количества ###
|
|
281
|
+
def count (self, where: Callable[[T], bool] = None) -> int:
|
|
282
|
+
"""
|
|
283
|
+
Количество элементов в массиве.
|
|
284
|
+
:param where: Функция выборки элементов. Вместо неё можно передать None, тогда будут возвращено общее
|
|
285
|
+
количество объектов в массиве. По умолчанию, None.
|
|
286
|
+
:type where: Callable[[T], bool] | None
|
|
287
|
+
:return: Количество элементов.
|
|
288
|
+
:rtype: int
|
|
289
|
+
"""
|
|
290
|
+
# Если массив пустой
|
|
291
|
+
if not self.__container:
|
|
292
|
+
# - то возвращаем 0
|
|
293
|
+
return 0
|
|
294
|
+
|
|
295
|
+
# Если функция выборки не задана
|
|
296
|
+
if where is None:
|
|
297
|
+
# - то возвращаем длину массива
|
|
298
|
+
return len(self.__container)
|
|
299
|
+
|
|
300
|
+
# Задаём счетчик
|
|
301
|
+
result = 0
|
|
302
|
+
|
|
303
|
+
# Для каждого элемента массива
|
|
304
|
+
for item in self.__container:
|
|
305
|
+
# - выполняем выборку
|
|
306
|
+
if where(item):
|
|
307
|
+
# -- и если элемент удовлетворяет условию, то увеличиваем счетчик на 1
|
|
308
|
+
result += 1
|
|
309
|
+
else:
|
|
310
|
+
# -- иначе переходим к следующему элементу
|
|
311
|
+
continue
|
|
312
|
+
|
|
313
|
+
# Возвращаем результат
|
|
314
|
+
return result
|
|
315
|
+
|
|
316
|
+
def is_exists (self, where: Callable[[T], bool]) -> bool:
|
|
317
|
+
"""
|
|
318
|
+
Проверка наличия элементов в массиве.
|
|
319
|
+
:param where: Функция выборки элементов.
|
|
320
|
+
:type where: Callable[[T], bool]
|
|
321
|
+
:return: True, если есть хотя бы один элемент, удовлетворяющий условию, иначе False.
|
|
322
|
+
:rtype: bool
|
|
323
|
+
"""
|
|
324
|
+
return self.count(where) > 0
|
|
325
|
+
|
|
326
|
+
### 2. Операторы выбора минимума и максимума ###
|
|
327
|
+
def min (self, by_value: Callable[[T], Any]) -> T | None:
|
|
328
|
+
"""
|
|
329
|
+
Минимальное значение.
|
|
330
|
+
:param by_value: Функция, возвращающая значение для сравнения.
|
|
331
|
+
:type by_value: Callable[[T], Any]
|
|
332
|
+
:return: Минимальное значение или None.
|
|
333
|
+
:rtype: T | None
|
|
334
|
+
"""
|
|
335
|
+
# Если массив пустой
|
|
336
|
+
if not self.__container:
|
|
337
|
+
# - то возвращаем None
|
|
338
|
+
return None
|
|
339
|
+
|
|
340
|
+
# Возвращаем минимальное значение
|
|
341
|
+
return reduce(
|
|
342
|
+
lambda current, value: value if current is None or by_value(value) < by_value(current) else current,
|
|
343
|
+
self.__container,
|
|
344
|
+
None
|
|
345
|
+
)
|
|
346
|
+
|
|
347
|
+
def max (self, by_value: Callable[[T], Any]) -> T | None:
|
|
348
|
+
"""
|
|
349
|
+
Максимальное значение.
|
|
350
|
+
:param by_value: Функция, возвращающая значение для сравнения.
|
|
351
|
+
:type by_value: Callable[[T], Any]
|
|
352
|
+
:return: Максимальное значение или None.
|
|
353
|
+
:rtype: T | None
|
|
354
|
+
"""
|
|
355
|
+
# Если массив пустой
|
|
356
|
+
if not self.__container:
|
|
357
|
+
# - то возвращаем None
|
|
358
|
+
return None
|
|
359
|
+
|
|
360
|
+
# Возвращаем максимальное значение
|
|
361
|
+
return reduce(
|
|
362
|
+
lambda current, value: value if current is None or by_value(value) > by_value(current) else current,
|
|
363
|
+
self.__container,
|
|
364
|
+
None
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
### 3. Операторы выбора элементов ###
|
|
368
|
+
def get_rows (self, where: Callable[[T], bool] = None) -> ObjectArray[T]:
|
|
369
|
+
"""
|
|
370
|
+
Выделяет из массива объектов объекты, удовлетворяющие условию.
|
|
371
|
+
:param where: Функция выборки объектов. Вместо неё можно передать None, тогда будут возвращены все объекты.
|
|
372
|
+
По умолчанию, None.
|
|
373
|
+
:type where: Callable[[T], bool] | None
|
|
374
|
+
:return: Массив объектов, удовлетворяющих условию.
|
|
375
|
+
:rtype: ObjectArray[T]
|
|
376
|
+
"""
|
|
377
|
+
# Если функция выборки не задана или массив пустой
|
|
378
|
+
if where is None or not self.__container:
|
|
379
|
+
# - то просто копируем массив
|
|
380
|
+
return ObjectArray(self.__container)
|
|
381
|
+
|
|
382
|
+
# Выбираем элементы, удовлетворяющие условию
|
|
383
|
+
items = [item for item in self.__container if where(item)]
|
|
384
|
+
|
|
385
|
+
# И создаём новый массив
|
|
386
|
+
return ObjectArray(items)
|
|
387
|
+
|
|
388
|
+
def get_row (self, where: Callable[[T], bool] = None) -> T | None:
|
|
389
|
+
"""
|
|
390
|
+
Выбирает из массива объектов объект, удовлетворяющий условию.
|
|
391
|
+
:param where: Функция выборки объектов. Вместо неё можно передать None, тогда будет возвращён первый объект.
|
|
392
|
+
По умолчанию, None.
|
|
393
|
+
:type where: Callable[[T], bool] | None
|
|
394
|
+
:return: Объект, удовлетворяющий условию или None, если объект не найден.
|
|
395
|
+
:rtype: T | None
|
|
396
|
+
"""
|
|
397
|
+
# Если массив пустой
|
|
398
|
+
if not self.__container:
|
|
399
|
+
# - то возвращаем None
|
|
400
|
+
return None
|
|
401
|
+
|
|
402
|
+
# Если функция выборки не задана
|
|
403
|
+
if where is None:
|
|
404
|
+
# - то возвращаем первый элемент
|
|
405
|
+
return self.__container[0]
|
|
406
|
+
|
|
407
|
+
# Выбираем элементы, удовлетворяющие условию
|
|
408
|
+
rows: ObjectArray[T] = self.get_rows(where)
|
|
409
|
+
|
|
410
|
+
# Если элементов не найдено
|
|
411
|
+
if len(rows) == 0:
|
|
412
|
+
# - то возвращаем None
|
|
413
|
+
return None
|
|
414
|
+
|
|
415
|
+
# Возвращаем первый найденный элемент
|
|
416
|
+
return rows[0]
|
|
417
|
+
|
|
418
|
+
def where (self, where: Callable[[T], bool]) -> T | ObjectArray[T] | None:
|
|
419
|
+
"""
|
|
420
|
+
Выбирает из массива объектов объекты, удовлетворяющие условию.
|
|
421
|
+
:param where: Функция выборки объектов.
|
|
422
|
+
:type where: Callable[[T], bool]
|
|
423
|
+
:return: Массив объектов, удовлетворяющих условию, если объектов несколько, или объект, удовлетворяющий условию,
|
|
424
|
+
если объект единственный, или None, если объект не найден.
|
|
425
|
+
:rtype: T | ObjectArray[T] | None
|
|
426
|
+
"""
|
|
427
|
+
# Получаем массив объектов, удовлетворяющих условию
|
|
428
|
+
rows: ObjectArray[T] = self.get_rows(where)
|
|
429
|
+
|
|
430
|
+
# Если массив пустой
|
|
431
|
+
if len(rows) == 0:
|
|
432
|
+
# - то возвращаем None
|
|
433
|
+
return None
|
|
434
|
+
# - если массив содержит только один элемент
|
|
435
|
+
elif len(rows) == 1:
|
|
436
|
+
# -- то возвращаем его
|
|
437
|
+
return rows[0]
|
|
438
|
+
# - если массив содержит более одного элемента
|
|
439
|
+
else:
|
|
440
|
+
# -- то возвращаем массив
|
|
441
|
+
return rows
|
|
442
|
+
|
|
443
|
+
def get_column (self, column_name: str, where: Callable[[T], bool] = None) -> ObjectArray[Any]:
|
|
444
|
+
"""
|
|
445
|
+
Выбирает из массива объектов значения свойства.
|
|
446
|
+
:param column_name: Имя свойства.
|
|
447
|
+
:type column_name: str
|
|
448
|
+
:param where: Функция выборки объектов. По умолчанию, None. Если None, то возвращаются свойства всех объектов.
|
|
449
|
+
:type where: Callable[[T], bool] | None
|
|
450
|
+
:return: Массив значений свойства.
|
|
451
|
+
:rtype: ObjectArray[Any]
|
|
452
|
+
"""
|
|
453
|
+
return self.get_column_callback(
|
|
454
|
+
lambda item: getattr(item, column_name, None) if hasattr(item, column_name) else None,
|
|
455
|
+
where
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
def get_column_callback (self, column: Callable[[T], Any], where: Callable[[T], bool] = None) -> ObjectArray[Any]:
|
|
459
|
+
"""
|
|
460
|
+
Выбирает из массива объектов значения свойства.
|
|
461
|
+
:param column: Функция получения значения свойства.
|
|
462
|
+
:type column: Callable[[T], Any]
|
|
463
|
+
:param where: Функция выборки объектов. По умолчанию, None. Если None, то возвращаются все объекты.
|
|
464
|
+
:type where: Callable[[T], bool] | None
|
|
465
|
+
:return: Массив значений свойства.
|
|
466
|
+
:rtype: ObjectArray[Any]
|
|
467
|
+
"""
|
|
468
|
+
# Получаем массив объектов, удовлетворяющих условию
|
|
469
|
+
items = [column(item) for item in self.__container if where is None or where(item)]
|
|
470
|
+
|
|
471
|
+
# Возвращаем массив
|
|
472
|
+
return ObjectArray(items)
|
|
473
|
+
|
|
474
|
+
def get_value (self, column: str, where: Callable[[T], bool] = None) -> Any | None:
|
|
475
|
+
"""
|
|
476
|
+
Получение значение единичного поля. Если полей по выборке будет больше одного, то вернёт первое из них.
|
|
477
|
+
:param column: Требуемый столбец.
|
|
478
|
+
:type column: str
|
|
479
|
+
:param where:Условие выборки., которое проверяет, подходит элемент или нет. Можно передать None, тогда будет
|
|
480
|
+
пробран весь массив. По умолчанию, None.
|
|
481
|
+
:type where: Callable[[T], bool] | None
|
|
482
|
+
:return: Значение поля или None, если поля нет.
|
|
483
|
+
:rtype: Any | None
|
|
484
|
+
"""
|
|
485
|
+
# Получаю колонку
|
|
486
|
+
result = self.get_column(column, where)
|
|
487
|
+
|
|
488
|
+
# Если колонка пустая
|
|
489
|
+
if len(result) == 0:
|
|
490
|
+
# -- возвращаю None
|
|
491
|
+
return None
|
|
492
|
+
|
|
493
|
+
# Возвращаю первый элемент колонки
|
|
494
|
+
return result[0]
|
|
495
|
+
|
|
496
|
+
### 4. Операторы удаления ###
|
|
497
|
+
def delete (self, where: Callable[[T], bool] = None) -> bool:
|
|
498
|
+
"""
|
|
499
|
+
Удаление элементов из массива.
|
|
500
|
+
:param where: Функция выборки элементов. По умолчанию, None. Если None, то будут удалены все элементы.
|
|
501
|
+
:type where: Callable[[T], bool] | None
|
|
502
|
+
:return: True, если удаление успешно, False, если удаление не удалось.
|
|
503
|
+
:rtype: bool
|
|
504
|
+
"""
|
|
505
|
+
# Если функция выборки не задана
|
|
506
|
+
if where is None:
|
|
507
|
+
# - то очищаем массив
|
|
508
|
+
self.clear()
|
|
509
|
+
# - и прерываем функцию
|
|
510
|
+
return True
|
|
511
|
+
|
|
512
|
+
# Получаем индексы элементов для удаления
|
|
513
|
+
remove_indices: list[int] = [i for i, item in enumerate(self.__container) if where(item)]
|
|
514
|
+
|
|
515
|
+
# Если индексы элементов для удаления не найдены
|
|
516
|
+
if not remove_indices:
|
|
517
|
+
# - то прерываем функцию
|
|
518
|
+
return False
|
|
519
|
+
|
|
520
|
+
# Проходим по индексам элементов для удаления в обратном порядке
|
|
521
|
+
for i in reversed(remove_indices):
|
|
522
|
+
# - удаляем элемент
|
|
523
|
+
del self.__container[i]
|
|
524
|
+
|
|
525
|
+
# Возвращаем True
|
|
526
|
+
return True
|
|
527
|
+
|
|
528
|
+
### 5. Операторы получения ###
|
|
529
|
+
def first (self, default: T | None = None) -> T | None:
|
|
530
|
+
"""
|
|
531
|
+
Безопасное получение первого элемента массива.
|
|
532
|
+
:param default: Значение по умолчанию или None. По умолчанию, None.
|
|
533
|
+
:return: Первый элемент массива, значение по умолчанию или None.
|
|
534
|
+
:rtype: T | None
|
|
535
|
+
"""
|
|
536
|
+
return self.__container[0] if self.__container else default
|
|
537
|
+
|
|
538
|
+
def last (self, default: T | None = None) -> T | None:
|
|
539
|
+
"""
|
|
540
|
+
Безопасное получение последнего элемента массива.
|
|
541
|
+
:param default: Значение по умолчанию или None. По умолчанию, None.
|
|
542
|
+
:return: Последний элемент массива, значение по умолчанию или None.
|
|
543
|
+
:rtype: T | None
|
|
544
|
+
"""
|
|
545
|
+
return self.__container[-1] if self.__container else default
|
|
546
|
+
|
|
547
|
+
def skip (self, count: int) -> ObjectArray[T]:
|
|
548
|
+
"""
|
|
549
|
+
Пропускает первые count элементов в массиве.
|
|
550
|
+
:param count: Количество пропускаемых элементов.
|
|
551
|
+
:type count: int
|
|
552
|
+
:return: Массив без первых count элементов.
|
|
553
|
+
:rtype: ObjectArray[T]
|
|
554
|
+
"""
|
|
555
|
+
# Если требуется пропустить отрицательное количество элементов или нуль
|
|
556
|
+
if count <= 0:
|
|
557
|
+
# - то просто копируем массив
|
|
558
|
+
return ObjectArray(self.__container)
|
|
559
|
+
|
|
560
|
+
# Если требуется пропустить больше элементов, чем есть в массиве
|
|
561
|
+
if count >= len(self.__container):
|
|
562
|
+
# - то возвращаем пустой массив
|
|
563
|
+
return ObjectArray()
|
|
564
|
+
|
|
565
|
+
# Возвращаем массив без первых count элементов
|
|
566
|
+
return ObjectArray(self.__container[count:], TypeCopyStrategy.IGNORE)
|
|
567
|
+
|
|
568
|
+
def take (self, count: int) -> ObjectArray[T]:
|
|
569
|
+
"""
|
|
570
|
+
Возвращает первые count элементов в массиве.
|
|
571
|
+
:param count: Количество возвращаемых элементов.
|
|
572
|
+
:type count: int
|
|
573
|
+
:return: Массив с первыми count элементами.
|
|
574
|
+
:rtype: ObjectArray[T]
|
|
575
|
+
"""
|
|
576
|
+
# Если требуется взять отрицательное количество элементов или нуль
|
|
577
|
+
if count <= 0:
|
|
578
|
+
# - то возвращаем пустой массив
|
|
579
|
+
return ObjectArray()
|
|
580
|
+
|
|
581
|
+
# Если требуется взять больше элементов, чем есть в массиве
|
|
582
|
+
if count >= len(self.__container):
|
|
583
|
+
# - то просто копируем массив
|
|
584
|
+
return ObjectArray(self.__container)
|
|
585
|
+
|
|
586
|
+
# Возвращаем массив с первыми count элементами
|
|
587
|
+
return ObjectArray(self.__container[:count], TypeCopyStrategy.IGNORE)
|
|
588
|
+
|
|
589
|
+
def skip_and_take (self, skip_count: int, take_count: int) -> ObjectArray[T]:
|
|
590
|
+
"""
|
|
591
|
+
Пропускает skip_count элементов и возвращает take_count элементов.
|
|
592
|
+
:param skip_count: Количество пропускаемых элементов.
|
|
593
|
+
:type skip_count: int
|
|
594
|
+
:param take_count: Количество возвращаемых элементов.
|
|
595
|
+
:type skip_count: int
|
|
596
|
+
:return: Массив с пропущенными skip_count элементами и take_count элементами.
|
|
597
|
+
:rtype: ObjectArray[T]
|
|
598
|
+
"""
|
|
599
|
+
# Если требуется пропустить отрицательное количество элементов
|
|
600
|
+
if skip_count < 0:
|
|
601
|
+
# - то приравниваем skip_count к нулю
|
|
602
|
+
skip_count = 0
|
|
603
|
+
|
|
604
|
+
# Если требуется взять отрицательное количество элементов
|
|
605
|
+
if take_count <= 0:
|
|
606
|
+
# - то возвращаем пустой массив
|
|
607
|
+
return ObjectArray([])
|
|
608
|
+
|
|
609
|
+
# Задаём начало
|
|
610
|
+
start = skip_count
|
|
611
|
+
# Задаём конец - начало + количество элементов, которые нужно взять
|
|
612
|
+
end = start + take_count
|
|
613
|
+
|
|
614
|
+
# Если начало больше длины массива
|
|
615
|
+
if start >= len(self.__container):
|
|
616
|
+
# - то возвращаем пустой массив
|
|
617
|
+
return ObjectArray([])
|
|
618
|
+
|
|
619
|
+
# Если конец больше длины массива
|
|
620
|
+
if end >= len(self.__container):
|
|
621
|
+
# - то обрезаем массив до конца
|
|
622
|
+
end = len(self.__container)
|
|
623
|
+
|
|
624
|
+
# Делаем обрезку массива по началу и концу и возвращаем результат
|
|
625
|
+
return ObjectArray(self.__container[start:end], TypeCopyStrategy.IGNORE)
|