absfuyu 4.1.1__py3-none-any.whl → 5.0.0__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.
Potentially problematic release.
This version of absfuyu might be problematic. Click here for more details.
- absfuyu/__init__.py +4 -4
- absfuyu/__main__.py +13 -1
- absfuyu/cli/__init__.py +4 -2
- absfuyu/cli/color.py +7 -0
- absfuyu/cli/do_group.py +9 -91
- absfuyu/cli/tool_group.py +136 -0
- absfuyu/config/__init__.py +17 -34
- absfuyu/core/__init__.py +49 -0
- absfuyu/core/baseclass.py +299 -0
- absfuyu/core/baseclass2.py +165 -0
- absfuyu/core/decorator.py +67 -0
- absfuyu/core/docstring.py +163 -0
- absfuyu/core/dummy_cli.py +67 -0
- absfuyu/core/dummy_func.py +47 -0
- absfuyu/dxt/__init__.py +42 -0
- absfuyu/dxt/dictext.py +201 -0
- absfuyu/dxt/dxt_support.py +79 -0
- absfuyu/dxt/intext.py +586 -0
- absfuyu/dxt/listext.py +508 -0
- absfuyu/dxt/strext.py +530 -0
- absfuyu/{extensions → extra}/__init__.py +3 -2
- absfuyu/extra/beautiful.py +251 -0
- absfuyu/{extensions/extra → extra}/data_analysis.py +51 -82
- absfuyu/fun/__init__.py +110 -135
- absfuyu/fun/tarot.py +9 -17
- absfuyu/game/__init__.py +6 -0
- absfuyu/game/game_stat.py +6 -0
- absfuyu/game/sudoku.py +7 -1
- absfuyu/game/tictactoe.py +12 -5
- absfuyu/game/wordle.py +14 -8
- absfuyu/general/__init__.py +6 -79
- absfuyu/general/content.py +22 -36
- absfuyu/general/generator.py +17 -42
- absfuyu/general/human.py +108 -228
- absfuyu/general/shape.py +1334 -0
- absfuyu/logger.py +8 -13
- absfuyu/pkg_data/__init__.py +137 -99
- absfuyu/pkg_data/deprecated.py +133 -0
- absfuyu/pkg_data/passwordlib_lzma.pkl +0 -0
- absfuyu/sort.py +6 -130
- absfuyu/tools/__init__.py +2 -2
- absfuyu/tools/checksum.py +44 -22
- absfuyu/tools/converter.py +82 -50
- absfuyu/tools/keygen.py +25 -30
- absfuyu/tools/obfuscator.py +246 -112
- absfuyu/tools/passwordlib.py +330 -0
- absfuyu/tools/shutdownizer.py +287 -0
- absfuyu/tools/web.py +2 -9
- absfuyu/util/__init__.py +15 -15
- absfuyu/util/api.py +10 -15
- absfuyu/util/json_method.py +7 -24
- absfuyu/util/lunar.py +3 -9
- absfuyu/util/path.py +22 -27
- absfuyu/util/performance.py +43 -67
- absfuyu/util/shorten_number.py +65 -14
- absfuyu/util/zipped.py +9 -15
- absfuyu-5.0.0.dist-info/METADATA +143 -0
- absfuyu-5.0.0.dist-info/RECORD +68 -0
- absfuyu/core.py +0 -57
- absfuyu/everything.py +0 -32
- absfuyu/extensions/beautiful.py +0 -188
- absfuyu/extensions/dev/__init__.py +0 -244
- absfuyu/extensions/dev/password_hash.py +0 -80
- absfuyu/extensions/dev/passwordlib.py +0 -258
- absfuyu/extensions/dev/project_starter.py +0 -60
- absfuyu/extensions/dev/shutdownizer.py +0 -156
- absfuyu/extensions/extra/__init__.py +0 -24
- absfuyu/fun/WGS.py +0 -134
- absfuyu/general/data_extension.py +0 -1796
- absfuyu/tools/stats.py +0 -226
- absfuyu/util/pkl.py +0 -67
- absfuyu-4.1.1.dist-info/METADATA +0 -121
- absfuyu-4.1.1.dist-info/RECORD +0 -61
- {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/WHEEL +0 -0
- {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/entry_points.txt +0 -0
- {absfuyu-4.1.1.dist-info → absfuyu-5.0.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,1796 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Absfuyu: Data extension
|
|
3
|
-
-----------------------
|
|
4
|
-
Extension for data type such as ``list``, ``str``, ``dict``, ...
|
|
5
|
-
|
|
6
|
-
Version: 1.15.2
|
|
7
|
-
Date updated: 06/01/2025 (dd/mm/yyyy)
|
|
8
|
-
|
|
9
|
-
Features:
|
|
10
|
-
---------
|
|
11
|
-
- DictExt
|
|
12
|
-
- IntNumber
|
|
13
|
-
- ListExt
|
|
14
|
-
- Text
|
|
15
|
-
|
|
16
|
-
Usage:
|
|
17
|
-
------
|
|
18
|
-
>>> from absfuyu.general.data_extension import DictExt, IntNumber, ListExt, Text
|
|
19
|
-
"""
|
|
20
|
-
|
|
21
|
-
# Module level
|
|
22
|
-
###########################################################################
|
|
23
|
-
__all__ = [
|
|
24
|
-
# Main
|
|
25
|
-
"Text",
|
|
26
|
-
"IntNumber",
|
|
27
|
-
"ListExt",
|
|
28
|
-
"DictExt",
|
|
29
|
-
# Sub
|
|
30
|
-
"Pow",
|
|
31
|
-
"ListREPR",
|
|
32
|
-
"ListNoDunder",
|
|
33
|
-
"DictBoolTrue",
|
|
34
|
-
"DictBoolFalse",
|
|
35
|
-
# "DictNoDunder",
|
|
36
|
-
# Dict format
|
|
37
|
-
"TextAnalyzeDictFormat",
|
|
38
|
-
# Support
|
|
39
|
-
"DictAnalyzeResult",
|
|
40
|
-
]
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
# Library
|
|
44
|
-
###########################################################################
|
|
45
|
-
import math
|
|
46
|
-
import operator
|
|
47
|
-
import random
|
|
48
|
-
from collections import Counter
|
|
49
|
-
from itertools import accumulate, chain, groupby
|
|
50
|
-
from typing import Any, Callable, NamedTuple, NotRequired, Self, TypedDict, Union
|
|
51
|
-
|
|
52
|
-
from deprecated.sphinx import versionadded, versionchanged
|
|
53
|
-
|
|
54
|
-
from absfuyu.general.generator import Charset, Generator
|
|
55
|
-
from absfuyu.logger import _compress_list_for_print, logger
|
|
56
|
-
from absfuyu.util import set_min, set_min_max
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Function
|
|
60
|
-
###########################################################################
|
|
61
|
-
def _dict_bool(dict_object: dict, option: bool) -> dict | None:
|
|
62
|
-
"""
|
|
63
|
-
Support function DictBool class
|
|
64
|
-
"""
|
|
65
|
-
out = dict()
|
|
66
|
-
for k, v in dict_object.items():
|
|
67
|
-
if v == option:
|
|
68
|
-
out[k] = v
|
|
69
|
-
if out:
|
|
70
|
-
return out
|
|
71
|
-
else:
|
|
72
|
-
return None
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
# MARK: Sub class
|
|
76
|
-
###########################################################################
|
|
77
|
-
class Pow:
|
|
78
|
-
"""Number power by a number"""
|
|
79
|
-
|
|
80
|
-
def __init__(self, number: int | float, power_by: int | float) -> None:
|
|
81
|
-
self.number = number
|
|
82
|
-
self.power_by = power_by
|
|
83
|
-
|
|
84
|
-
def __str__(self) -> str:
|
|
85
|
-
if self.power_by == 1:
|
|
86
|
-
return str(self.number)
|
|
87
|
-
else:
|
|
88
|
-
return f"{self.number}^{self.power_by}"
|
|
89
|
-
# return f"{self.__class__.__name__}({self.number}, {self.power_by})"
|
|
90
|
-
|
|
91
|
-
def __repr__(self) -> str:
|
|
92
|
-
return self.__str__()
|
|
93
|
-
|
|
94
|
-
def to_list(self) -> list[int | float]:
|
|
95
|
-
"""
|
|
96
|
-
Convert into list
|
|
97
|
-
|
|
98
|
-
:rtype: list[int | float]
|
|
99
|
-
"""
|
|
100
|
-
return [self.number] * int(self.power_by)
|
|
101
|
-
|
|
102
|
-
def calculate(self) -> float:
|
|
103
|
-
"""
|
|
104
|
-
Calculate the ``self.number`` to the power of ``self.power_by``
|
|
105
|
-
|
|
106
|
-
:rtype: float
|
|
107
|
-
"""
|
|
108
|
-
# return self.number**self.power_by
|
|
109
|
-
return math.pow(self.number, self.power_by)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
class ListREPR(list):
|
|
113
|
-
"""Show ``list`` in shorter form"""
|
|
114
|
-
|
|
115
|
-
def __repr__(self) -> str:
|
|
116
|
-
return _compress_list_for_print(self, 9)
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
class ListNoDunder(list[str]):
|
|
120
|
-
"""Use with ``object.__dir__()``"""
|
|
121
|
-
|
|
122
|
-
def __repr__(self) -> str:
|
|
123
|
-
out = [x for x in self if not x.startswith("__")]
|
|
124
|
-
return out.__repr__()
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class DictBoolTrue(dict[Any, bool]):
|
|
128
|
-
"""Only show items when ``values == True`` in ``__repr__()``"""
|
|
129
|
-
|
|
130
|
-
def __repr__(self) -> str:
|
|
131
|
-
temp = self.copy()
|
|
132
|
-
return _dict_bool(temp, True).__repr__()
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
class DictBoolFalse(dict[Any, bool]):
|
|
136
|
-
"""Only show items when ``values == False`` in ``__repr__()``"""
|
|
137
|
-
|
|
138
|
-
def __repr__(self) -> str:
|
|
139
|
-
temp = self.copy()
|
|
140
|
-
return _dict_bool(temp, False).__repr__()
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
# class DictNoDunder(dict): # W.I.P
|
|
144
|
-
# """Remove dunder methods in ``__repr__()`` of dict"""
|
|
145
|
-
|
|
146
|
-
# def __repr__(self) -> str:
|
|
147
|
-
# temp = self.copy()
|
|
148
|
-
# out = dict()
|
|
149
|
-
# for k, v in temp.items():
|
|
150
|
-
# if not str(k).startswith("__"):
|
|
151
|
-
# out.__setattr__(k, v)
|
|
152
|
-
# return out.__repr__()
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class TextAnalyzeDictFormat(TypedDict):
|
|
156
|
-
"""
|
|
157
|
-
Dict format for ``Text.analyze()`` method
|
|
158
|
-
|
|
159
|
-
Parameters
|
|
160
|
-
----------
|
|
161
|
-
digit : int
|
|
162
|
-
Number of digit characters
|
|
163
|
-
|
|
164
|
-
uppercase : int
|
|
165
|
-
Number of uppercase characters
|
|
166
|
-
|
|
167
|
-
lowercase : int
|
|
168
|
-
Number of lowercase characters
|
|
169
|
-
|
|
170
|
-
other : int
|
|
171
|
-
Number of other printable characters
|
|
172
|
-
|
|
173
|
-
is_pangram : NotRequired[bool]
|
|
174
|
-
Is a pangram (Not required)
|
|
175
|
-
|
|
176
|
-
is_palindrome : NotRequired[bool]
|
|
177
|
-
Is a palindrome (Not required)
|
|
178
|
-
"""
|
|
179
|
-
|
|
180
|
-
digit: int
|
|
181
|
-
uppercase: int
|
|
182
|
-
lowercase: int
|
|
183
|
-
other: int
|
|
184
|
-
is_pangram: NotRequired[bool]
|
|
185
|
-
is_palindrome: NotRequired[bool]
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
class DictAnalyzeResult(NamedTuple):
|
|
189
|
-
"""
|
|
190
|
-
Result for ``DictExt.analyze()``
|
|
191
|
-
"""
|
|
192
|
-
|
|
193
|
-
max_value: int | float
|
|
194
|
-
min_value: int | float
|
|
195
|
-
max_list: list
|
|
196
|
-
min_list: list
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
# Class
|
|
200
|
-
###########################################################################
|
|
201
|
-
# MARK: Text
|
|
202
|
-
class Text(str):
|
|
203
|
-
"""
|
|
204
|
-
``str`` extension
|
|
205
|
-
"""
|
|
206
|
-
|
|
207
|
-
def divide(self, string_split_size: int = 60) -> list[str]:
|
|
208
|
-
"""
|
|
209
|
-
Divide long string into smaller size
|
|
210
|
-
|
|
211
|
-
Parameters
|
|
212
|
-
----------
|
|
213
|
-
string_split_size : int
|
|
214
|
-
Divide string every ``x`` character
|
|
215
|
-
(Default: ``60``)
|
|
216
|
-
|
|
217
|
-
Returns
|
|
218
|
-
-------
|
|
219
|
-
list[str]
|
|
220
|
-
A list in which each item is a smaller
|
|
221
|
-
string with the size of ``string_split_size``
|
|
222
|
-
(need to be concaternate later)
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
Example:
|
|
226
|
-
--------
|
|
227
|
-
>>> test = Text("This is an extremely long line of text!")
|
|
228
|
-
>>> test.divide(string_split_size=20)
|
|
229
|
-
['This is an extremely', ' long line of text!']
|
|
230
|
-
"""
|
|
231
|
-
temp = str(self)
|
|
232
|
-
output = []
|
|
233
|
-
while len(temp) != 0:
|
|
234
|
-
output.append(temp[:string_split_size])
|
|
235
|
-
temp = temp[string_split_size:]
|
|
236
|
-
return output
|
|
237
|
-
|
|
238
|
-
def divide_with_variable(
|
|
239
|
-
self,
|
|
240
|
-
split_size: int = 60,
|
|
241
|
-
split_var_len: int = 12,
|
|
242
|
-
custom_var_name: str | None = None,
|
|
243
|
-
) -> list[str]:
|
|
244
|
-
"""
|
|
245
|
-
Divide long string into smaller size,
|
|
246
|
-
then assign a random variable to splited
|
|
247
|
-
string for later use
|
|
248
|
-
|
|
249
|
-
Parameters
|
|
250
|
-
----------
|
|
251
|
-
split_size : int
|
|
252
|
-
Divide string every ``x`` character
|
|
253
|
-
(Default: ``60``)
|
|
254
|
-
|
|
255
|
-
split_var_len : int
|
|
256
|
-
Length of variable name assigned to each item
|
|
257
|
-
(Default: ``12``)
|
|
258
|
-
|
|
259
|
-
custom_var_name : str
|
|
260
|
-
Custom variable name when join string
|
|
261
|
-
|
|
262
|
-
Returns
|
|
263
|
-
-------
|
|
264
|
-
list[str]
|
|
265
|
-
A list in which each item is a smaller
|
|
266
|
-
string with the size of ``split_size``
|
|
267
|
-
and a way to concaternate them (when using ``print()``)
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
Example:
|
|
271
|
-
--------
|
|
272
|
-
>>> test = Text("This is an extremely long line of text!")
|
|
273
|
-
>>> test.divide_with_variable(split_size=20)
|
|
274
|
-
[
|
|
275
|
-
"qNTCnmkFPTJg='This is an extremely'",
|
|
276
|
-
"vkmLBUykYYDG=' long line of text!'",
|
|
277
|
-
'sBoSwEfoxBIH=qNTCnmkFPTJg+vkmLBUykYYDG',
|
|
278
|
-
'sBoSwEfoxBIH'
|
|
279
|
-
]
|
|
280
|
-
|
|
281
|
-
>>> test = Text("This is an extremely long line of text!")
|
|
282
|
-
>>> test.divide_with_variable(split_size=20, custom_var_name="test")
|
|
283
|
-
[
|
|
284
|
-
"test1='This is an extremely'",
|
|
285
|
-
"test2=' long line of text!'",
|
|
286
|
-
'test=test1+test2',
|
|
287
|
-
'test'
|
|
288
|
-
]
|
|
289
|
-
"""
|
|
290
|
-
|
|
291
|
-
temp = self.divide(split_size)
|
|
292
|
-
output = []
|
|
293
|
-
|
|
294
|
-
# split variable
|
|
295
|
-
splt_len = len(temp)
|
|
296
|
-
|
|
297
|
-
if custom_var_name is None:
|
|
298
|
-
splt_name = Generator.generate_string(
|
|
299
|
-
charset=Charset.ALPHABET, size=split_var_len, times=splt_len + 1
|
|
300
|
-
)
|
|
301
|
-
for i in range(splt_len):
|
|
302
|
-
output.append(f"{splt_name[i]}='{temp[i]}'")
|
|
303
|
-
else:
|
|
304
|
-
for i in range(splt_len):
|
|
305
|
-
output.append(f"{custom_var_name}{i + 1}='{temp[i]}'")
|
|
306
|
-
|
|
307
|
-
# joined variable
|
|
308
|
-
temp = []
|
|
309
|
-
if custom_var_name is None:
|
|
310
|
-
for i in range(splt_len):
|
|
311
|
-
if i == 0:
|
|
312
|
-
temp.append(f"{splt_name[-1]}=")
|
|
313
|
-
if i == splt_len - 1:
|
|
314
|
-
temp.append(f"{splt_name[i]}")
|
|
315
|
-
else:
|
|
316
|
-
temp.append(f"{splt_name[i]}+")
|
|
317
|
-
else:
|
|
318
|
-
for i in range(splt_len):
|
|
319
|
-
if i == 0:
|
|
320
|
-
temp.append(f"{custom_var_name}=")
|
|
321
|
-
if i == splt_len - 1:
|
|
322
|
-
temp.append(f"{custom_var_name}{i + 1}")
|
|
323
|
-
else:
|
|
324
|
-
temp.append(f"{custom_var_name}{i + 1}+")
|
|
325
|
-
|
|
326
|
-
output.append("".join(temp))
|
|
327
|
-
if custom_var_name is None:
|
|
328
|
-
output.append(splt_name[-1])
|
|
329
|
-
else:
|
|
330
|
-
output.append(custom_var_name)
|
|
331
|
-
|
|
332
|
-
return output
|
|
333
|
-
|
|
334
|
-
@versionchanged(version="3.3.0", reason="Update functionality")
|
|
335
|
-
def analyze(self, full: bool = False) -> TextAnalyzeDictFormat:
|
|
336
|
-
"""
|
|
337
|
-
String analyze (count number of type of character)
|
|
338
|
-
|
|
339
|
-
Parameters
|
|
340
|
-
----------
|
|
341
|
-
full : bool
|
|
342
|
-
Full analyze when ``True``
|
|
343
|
-
(Default: ``False``)
|
|
344
|
-
|
|
345
|
-
Returns
|
|
346
|
-
-------
|
|
347
|
-
dict | TextAnalyzeDictFormat
|
|
348
|
-
A dictionary contains number of digit character,
|
|
349
|
-
uppercase character, lowercase character, and
|
|
350
|
-
special character
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
Example:
|
|
354
|
-
--------
|
|
355
|
-
>>> test = Text("Random T3xt!")
|
|
356
|
-
>>> test.analyze()
|
|
357
|
-
{'digit': 1, 'uppercase': 2, 'lowercase': 7, 'other': 2}
|
|
358
|
-
"""
|
|
359
|
-
|
|
360
|
-
temp = self
|
|
361
|
-
|
|
362
|
-
detail: TextAnalyzeDictFormat = {
|
|
363
|
-
"digit": 0,
|
|
364
|
-
"uppercase": 0,
|
|
365
|
-
"lowercase": 0,
|
|
366
|
-
"other": 0,
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
for x in temp:
|
|
370
|
-
if ord(x) in range(48, 58): # num
|
|
371
|
-
detail["digit"] += 1
|
|
372
|
-
elif ord(x) in range(65, 91): # cap
|
|
373
|
-
detail["uppercase"] += 1
|
|
374
|
-
elif ord(x) in range(97, 123): # low
|
|
375
|
-
detail["lowercase"] += 1
|
|
376
|
-
else:
|
|
377
|
-
detail["other"] += 1
|
|
378
|
-
|
|
379
|
-
if full:
|
|
380
|
-
detail["is_palindrome"] = self.is_palindrome()
|
|
381
|
-
detail["is_pangram"] = self.is_pangram()
|
|
382
|
-
|
|
383
|
-
return detail
|
|
384
|
-
|
|
385
|
-
def reverse(self) -> Self:
|
|
386
|
-
"""
|
|
387
|
-
Reverse the string
|
|
388
|
-
|
|
389
|
-
Returns
|
|
390
|
-
-------
|
|
391
|
-
Text
|
|
392
|
-
Reversed string
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
Example:
|
|
396
|
-
--------
|
|
397
|
-
>>> test = Text("Hello, World!")
|
|
398
|
-
>>> test.reverse()
|
|
399
|
-
'!dlroW ,olleH'
|
|
400
|
-
"""
|
|
401
|
-
return self.__class__(self[::-1])
|
|
402
|
-
|
|
403
|
-
def is_pangram(self) -> bool:
|
|
404
|
-
"""
|
|
405
|
-
Check if string is a pangram
|
|
406
|
-
|
|
407
|
-
A pangram is a unique sentence in which
|
|
408
|
-
every letter of the alphabet is used at least once
|
|
409
|
-
|
|
410
|
-
Returns
|
|
411
|
-
-------
|
|
412
|
-
bool
|
|
413
|
-
| ``True`` if string is a pangram
|
|
414
|
-
| ``False`` if string is not a pangram
|
|
415
|
-
"""
|
|
416
|
-
text = self
|
|
417
|
-
alphabet = set("abcdefghijklmnopqrstuvwxyz")
|
|
418
|
-
return not set(alphabet) - set(text.lower())
|
|
419
|
-
|
|
420
|
-
def is_palindrome(self) -> bool:
|
|
421
|
-
"""
|
|
422
|
-
Check if string is a palindrome
|
|
423
|
-
|
|
424
|
-
A palindrome is a word, verse, or sentence
|
|
425
|
-
or a number that reads the same backward or forward
|
|
426
|
-
|
|
427
|
-
Returns
|
|
428
|
-
-------
|
|
429
|
-
bool
|
|
430
|
-
| ``True`` if string is a palindrome
|
|
431
|
-
| ``False`` if string is not a palindrome
|
|
432
|
-
"""
|
|
433
|
-
text = self
|
|
434
|
-
# Use string slicing [start:end:step]
|
|
435
|
-
return text == text[::-1]
|
|
436
|
-
|
|
437
|
-
def to_hex(self, raw: bool = False) -> str:
|
|
438
|
-
r"""
|
|
439
|
-
Convert string to hex form
|
|
440
|
-
|
|
441
|
-
Parameters
|
|
442
|
-
----------
|
|
443
|
-
raw : bool
|
|
444
|
-
| ``False``: hex string in the form of ``\x`` (default)
|
|
445
|
-
| ``True``: normal hex string
|
|
446
|
-
|
|
447
|
-
Returns
|
|
448
|
-
-------
|
|
449
|
-
str
|
|
450
|
-
Hexed string
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
Example:
|
|
454
|
-
--------
|
|
455
|
-
>>> test = Text("Hello, World!")
|
|
456
|
-
>>> test.to_hex()
|
|
457
|
-
'\\x48\\x65\\x6c\\x6c\\x6f\\x2c\\x20\\x57\\x6f\\x72\\x6c\\x64\\x21'
|
|
458
|
-
"""
|
|
459
|
-
text = self
|
|
460
|
-
|
|
461
|
-
byte_str = text.encode("utf-8")
|
|
462
|
-
hex_str = byte_str.hex()
|
|
463
|
-
|
|
464
|
-
if not raw:
|
|
465
|
-
temp = []
|
|
466
|
-
str_len = len(hex_str)
|
|
467
|
-
|
|
468
|
-
for i in range(str_len):
|
|
469
|
-
if i % 2 == 0:
|
|
470
|
-
temp.append("\\x")
|
|
471
|
-
temp.append(hex_str[i])
|
|
472
|
-
return "".join(temp)
|
|
473
|
-
else:
|
|
474
|
-
return hex_str
|
|
475
|
-
|
|
476
|
-
def random_capslock(self, probability: int = 50) -> Self:
|
|
477
|
-
"""
|
|
478
|
-
Randomly capslock letter in string
|
|
479
|
-
|
|
480
|
-
Parameters
|
|
481
|
-
----------
|
|
482
|
-
probability : int
|
|
483
|
-
Probability in range [0, 100]
|
|
484
|
-
(Default: ``50``)
|
|
485
|
-
|
|
486
|
-
Returns
|
|
487
|
-
-------
|
|
488
|
-
Text
|
|
489
|
-
Random capslocked text
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
Example:
|
|
493
|
-
--------
|
|
494
|
-
>>> test = Text("This is an extremely long line of text!")
|
|
495
|
-
>>> test.random_capslock()
|
|
496
|
-
'tHis iS An ExtREmELY loNg liNE oF tExT!'
|
|
497
|
-
"""
|
|
498
|
-
probability = int(set_min_max(probability))
|
|
499
|
-
text = self.lower()
|
|
500
|
-
|
|
501
|
-
temp = []
|
|
502
|
-
for x in text:
|
|
503
|
-
if random.randint(1, 100) <= probability:
|
|
504
|
-
x = x.upper()
|
|
505
|
-
temp.append(x)
|
|
506
|
-
logger.debug(temp)
|
|
507
|
-
return self.__class__("".join(temp))
|
|
508
|
-
|
|
509
|
-
def reverse_capslock(self) -> Self:
|
|
510
|
-
"""
|
|
511
|
-
Reverse capslock in string
|
|
512
|
-
|
|
513
|
-
Returns
|
|
514
|
-
-------
|
|
515
|
-
Text
|
|
516
|
-
Reversed capslock ``Text``
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
Example:
|
|
520
|
-
--------
|
|
521
|
-
>>> test = Text("Foo")
|
|
522
|
-
>>> test.reverse_capslock()
|
|
523
|
-
'fOO'
|
|
524
|
-
"""
|
|
525
|
-
temp = list(self)
|
|
526
|
-
for i, x in enumerate(temp):
|
|
527
|
-
if x.isupper():
|
|
528
|
-
temp[i] = x.lower()
|
|
529
|
-
else:
|
|
530
|
-
temp[i] = x.upper()
|
|
531
|
-
return self.__class__("".join(temp))
|
|
532
|
-
|
|
533
|
-
def to_list(self) -> list[str]:
|
|
534
|
-
"""
|
|
535
|
-
Convert into list
|
|
536
|
-
|
|
537
|
-
Returns
|
|
538
|
-
-------
|
|
539
|
-
list[str]
|
|
540
|
-
List of string
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
Example:
|
|
544
|
-
--------
|
|
545
|
-
>>> test = Text("test")
|
|
546
|
-
>>> test.to_list()
|
|
547
|
-
['t', 'e', 's', 't']
|
|
548
|
-
"""
|
|
549
|
-
return list(self)
|
|
550
|
-
|
|
551
|
-
@versionadded(version="3.3.0")
|
|
552
|
-
def to_listext(self) -> "ListExt":
|
|
553
|
-
"""
|
|
554
|
-
Convert into ``ListExt``
|
|
555
|
-
|
|
556
|
-
Returns
|
|
557
|
-
-------
|
|
558
|
-
ListExt[str]
|
|
559
|
-
List of string
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
Example:
|
|
563
|
-
--------
|
|
564
|
-
>>> test = Text("test")
|
|
565
|
-
>>> test.to_listext()
|
|
566
|
-
['t', 'e', 's', 't']
|
|
567
|
-
"""
|
|
568
|
-
return ListExt(self)
|
|
569
|
-
|
|
570
|
-
@versionadded(version="3.3.0")
|
|
571
|
-
def count_pattern(self, pattern: str, ignore_capslock: bool = False) -> int:
|
|
572
|
-
"""
|
|
573
|
-
Returns how many times ``pattern`` appears in text
|
|
574
|
-
(Inspired by hackerrank exercise)
|
|
575
|
-
|
|
576
|
-
Parameters
|
|
577
|
-
----------
|
|
578
|
-
pattern : str
|
|
579
|
-
Pattern to count
|
|
580
|
-
|
|
581
|
-
ignore_capslock : bool
|
|
582
|
-
Ignore the pattern uppercase or lowercase
|
|
583
|
-
(Default: ``False`` - Exact match)
|
|
584
|
-
|
|
585
|
-
Returns
|
|
586
|
-
-------
|
|
587
|
-
int
|
|
588
|
-
How many times pattern appeared
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
Example:
|
|
592
|
-
--------
|
|
593
|
-
>>> Text("test").count_pattern("t")
|
|
594
|
-
2
|
|
595
|
-
"""
|
|
596
|
-
if len(pattern) > len(self):
|
|
597
|
-
raise ValueError(f"len(pattern) must not larger than {len(self)}")
|
|
598
|
-
|
|
599
|
-
temp = str(self)
|
|
600
|
-
if ignore_capslock:
|
|
601
|
-
pattern = pattern.lower()
|
|
602
|
-
temp = temp.lower()
|
|
603
|
-
|
|
604
|
-
out = [
|
|
605
|
-
1
|
|
606
|
-
for i in range(len(temp) - len(pattern) + 1)
|
|
607
|
-
if temp[i : i + len(pattern)] == pattern
|
|
608
|
-
]
|
|
609
|
-
return sum(out)
|
|
610
|
-
|
|
611
|
-
@versionadded(version="3.3.0")
|
|
612
|
-
def hapax(self, strict: bool = False) -> list[str]:
|
|
613
|
-
"""
|
|
614
|
-
A hapax legomenon (often abbreviated to hapax)
|
|
615
|
-
is a word which occurs only once in either
|
|
616
|
-
the written record of a language, the works of
|
|
617
|
-
an author, or in a single text.
|
|
618
|
-
|
|
619
|
-
This function returns a list of hapaxes (if any)
|
|
620
|
-
(Lettercase is ignored)
|
|
621
|
-
|
|
622
|
-
Parameters
|
|
623
|
-
----------
|
|
624
|
-
strict : bool
|
|
625
|
-
Remove all special characters before checking for hapax
|
|
626
|
-
(Default: ``False``)
|
|
627
|
-
|
|
628
|
-
Returns
|
|
629
|
-
-------
|
|
630
|
-
list[str]
|
|
631
|
-
A list of hapaxes
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
Example:
|
|
635
|
-
--------
|
|
636
|
-
>>> test = Text("A a. a, b c c= C| d d")
|
|
637
|
-
>>> test.hapax()
|
|
638
|
-
['a', 'a.', 'a,', 'b', 'c', 'c=', 'c|']
|
|
639
|
-
|
|
640
|
-
>>> test.hapax(strict=True)
|
|
641
|
-
['b']
|
|
642
|
-
"""
|
|
643
|
-
word_list: list[str] = self.lower().split()
|
|
644
|
-
if strict:
|
|
645
|
-
remove_characters: list[str] = list(r"\"'.,:;|()[]{}\/!@#$%^&*-_=+?<>`~")
|
|
646
|
-
temp = str(self)
|
|
647
|
-
for x in remove_characters:
|
|
648
|
-
temp = temp.replace(x, "")
|
|
649
|
-
word_list = temp.lower().split()
|
|
650
|
-
|
|
651
|
-
hapaxes = filter(lambda x: word_list.count(x) == 1, word_list)
|
|
652
|
-
return list(hapaxes)
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
# MARK: IntNumber
|
|
656
|
-
class IntNumber(int):
|
|
657
|
-
"""
|
|
658
|
-
``int`` extension
|
|
659
|
-
"""
|
|
660
|
-
|
|
661
|
-
# convert stuff
|
|
662
|
-
def to_binary(self) -> str:
|
|
663
|
-
"""
|
|
664
|
-
Convert to binary number
|
|
665
|
-
|
|
666
|
-
Returns
|
|
667
|
-
-------
|
|
668
|
-
str
|
|
669
|
-
Binary number
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
Example:
|
|
673
|
-
--------
|
|
674
|
-
>>> test = IntNumber(10)
|
|
675
|
-
>>> test.to_binary()
|
|
676
|
-
'1010'
|
|
677
|
-
"""
|
|
678
|
-
return format(self, "b")
|
|
679
|
-
|
|
680
|
-
def to_celcius_degree(self) -> float:
|
|
681
|
-
"""
|
|
682
|
-
Convert into Celcius degree as if ``self`` is Fahrenheit degree
|
|
683
|
-
|
|
684
|
-
Returns
|
|
685
|
-
-------
|
|
686
|
-
float
|
|
687
|
-
Celcius degree
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
Example:
|
|
691
|
-
--------
|
|
692
|
-
>>> test = IntNumber(10)
|
|
693
|
-
>>> test.to_celcius_degree()
|
|
694
|
-
-12.222222222222221
|
|
695
|
-
"""
|
|
696
|
-
c_degree = (self - 32) / 1.8
|
|
697
|
-
return c_degree
|
|
698
|
-
|
|
699
|
-
def to_fahrenheit_degree(self) -> float:
|
|
700
|
-
"""
|
|
701
|
-
Convert into Fahrenheit degree as if ``self`` is Celcius degree
|
|
702
|
-
|
|
703
|
-
Returns
|
|
704
|
-
-------
|
|
705
|
-
float
|
|
706
|
-
Fahrenheit degree
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
Example:
|
|
710
|
-
--------
|
|
711
|
-
>>> test = IntNumber(10)
|
|
712
|
-
>>> test.to_fahrenheit_degree()
|
|
713
|
-
50.0
|
|
714
|
-
"""
|
|
715
|
-
f_degree = (self * 1.8) + 32
|
|
716
|
-
return f_degree
|
|
717
|
-
|
|
718
|
-
# is_stuff
|
|
719
|
-
def is_even(self) -> bool:
|
|
720
|
-
"""
|
|
721
|
-
An even number is a number which divisible by 2
|
|
722
|
-
|
|
723
|
-
Returns
|
|
724
|
-
-------
|
|
725
|
-
bool
|
|
726
|
-
| ``True`` if an even number
|
|
727
|
-
| ``False`` if not an even number
|
|
728
|
-
"""
|
|
729
|
-
return self % 2 == 0
|
|
730
|
-
|
|
731
|
-
def is_prime(self) -> bool:
|
|
732
|
-
"""
|
|
733
|
-
Check if the integer is a prime number or not
|
|
734
|
-
|
|
735
|
-
A prime number is a natural number greater than ``1``
|
|
736
|
-
that is not a product of two smaller natural numbers.
|
|
737
|
-
A natural number greater than ``1`` that is not prime
|
|
738
|
-
is called a composite number.
|
|
739
|
-
|
|
740
|
-
Returns
|
|
741
|
-
-------
|
|
742
|
-
bool
|
|
743
|
-
| ``True`` if a prime number
|
|
744
|
-
| ``False`` if not a prime number
|
|
745
|
-
"""
|
|
746
|
-
number = self
|
|
747
|
-
|
|
748
|
-
if number <= 1:
|
|
749
|
-
return False
|
|
750
|
-
for i in range(2, int(math.sqrt(number)) + 1): # divisor range
|
|
751
|
-
if number % i == 0:
|
|
752
|
-
return False
|
|
753
|
-
return True
|
|
754
|
-
|
|
755
|
-
def is_twisted_prime(self) -> bool:
|
|
756
|
-
"""
|
|
757
|
-
A number is said to be twisted prime if
|
|
758
|
-
it is a prime number and
|
|
759
|
-
reverse of the number is also a prime number
|
|
760
|
-
|
|
761
|
-
Returns
|
|
762
|
-
-------
|
|
763
|
-
bool
|
|
764
|
-
| ``True`` if a twisted prime number
|
|
765
|
-
| ``False`` if not a twisted prime number
|
|
766
|
-
"""
|
|
767
|
-
prime = self.is_prime()
|
|
768
|
-
logger.debug(f"is prime: {prime}")
|
|
769
|
-
rev = self.reverse().is_prime()
|
|
770
|
-
logger.debug(f"is prime when reversed: {rev}")
|
|
771
|
-
return prime and rev
|
|
772
|
-
|
|
773
|
-
def is_perfect(self) -> bool:
|
|
774
|
-
"""
|
|
775
|
-
Check if integer is perfect number
|
|
776
|
-
|
|
777
|
-
Perfect number: a positive integer that is
|
|
778
|
-
equal to the sum of its proper divisors.
|
|
779
|
-
The smallest perfect number is ``6``, which is
|
|
780
|
-
the sum of ``1``, ``2``, and ``3``.
|
|
781
|
-
Other perfect numbers are ``28``, ``496``, and ``8,128``.
|
|
782
|
-
|
|
783
|
-
Returns
|
|
784
|
-
-------
|
|
785
|
-
bool
|
|
786
|
-
| ``True`` if a perfect number
|
|
787
|
-
| ``False`` if not a perfect number
|
|
788
|
-
"""
|
|
789
|
-
# ---
|
|
790
|
-
"""
|
|
791
|
-
# List of known perfect number
|
|
792
|
-
# Source: https://en.wikipedia.org/wiki/List_of_Mersenne_primes_and_perfect_numbers
|
|
793
|
-
perfect_number_index = [
|
|
794
|
-
2, 3, 5, 7,
|
|
795
|
-
13, 17, 19, 31, 61, 89,
|
|
796
|
-
107, 127, 521, 607,
|
|
797
|
-
1279, 2203, 2281, 3217, 4253, 4423, 9689, 9941,
|
|
798
|
-
11_213, 19_937, 21_701, 23_209, 44_497, 86_243,
|
|
799
|
-
110_503, 132_049, 216_091, 756_839, 859_433,
|
|
800
|
-
# 1_257_787, 1_398_269, 2_976_221, 3_021_377, 6_972_593,
|
|
801
|
-
# 13_466_917, 20_996_011, 24_036_583, 25_964_951,
|
|
802
|
-
# 30_402_457, 32_582_657, 37_156_667, 42_643_801,
|
|
803
|
-
# 43_112_609, 57_885_161,
|
|
804
|
-
## 74_207_281, 77_232_917, 82_589_933
|
|
805
|
-
]
|
|
806
|
-
perfect_number = []
|
|
807
|
-
for x in perfect_number_index:
|
|
808
|
-
# a perfect number have a form of (2**(n-1))*((2**n)-1)
|
|
809
|
-
perfect_number.append((2**(x-1))*((2**x)-1))
|
|
810
|
-
"""
|
|
811
|
-
number = int(self)
|
|
812
|
-
|
|
813
|
-
perfect_number = [
|
|
814
|
-
6,
|
|
815
|
-
28,
|
|
816
|
-
496,
|
|
817
|
-
8128,
|
|
818
|
-
33_550_336,
|
|
819
|
-
8_589_869_056,
|
|
820
|
-
137_438_691_328,
|
|
821
|
-
2_305_843_008_139_952_128,
|
|
822
|
-
]
|
|
823
|
-
|
|
824
|
-
if number in perfect_number:
|
|
825
|
-
return True
|
|
826
|
-
|
|
827
|
-
elif number < perfect_number[-1]:
|
|
828
|
-
return False
|
|
829
|
-
|
|
830
|
-
else:
|
|
831
|
-
# Faster way to check
|
|
832
|
-
perfect_number_index: list[int] = [
|
|
833
|
-
61,
|
|
834
|
-
89,
|
|
835
|
-
107,
|
|
836
|
-
127,
|
|
837
|
-
521,
|
|
838
|
-
607,
|
|
839
|
-
1279,
|
|
840
|
-
2203,
|
|
841
|
-
2281,
|
|
842
|
-
3217,
|
|
843
|
-
4253,
|
|
844
|
-
4423,
|
|
845
|
-
9689,
|
|
846
|
-
9941,
|
|
847
|
-
11_213,
|
|
848
|
-
19_937,
|
|
849
|
-
21_701,
|
|
850
|
-
23_209,
|
|
851
|
-
44_497,
|
|
852
|
-
86_243,
|
|
853
|
-
110_503,
|
|
854
|
-
132_049,
|
|
855
|
-
216_091,
|
|
856
|
-
756_839,
|
|
857
|
-
859_433,
|
|
858
|
-
1_257_787,
|
|
859
|
-
# 1_398_269,
|
|
860
|
-
# 2_976_221,
|
|
861
|
-
# 3_021_377,
|
|
862
|
-
# 6_972_593,
|
|
863
|
-
# 13_466_917,
|
|
864
|
-
# 20_996_011,
|
|
865
|
-
# 24_036_583,
|
|
866
|
-
# 25_964_951,
|
|
867
|
-
# 30_402_457,
|
|
868
|
-
# 32_582_657,
|
|
869
|
-
# 37_156_667,
|
|
870
|
-
# 42_643_801,
|
|
871
|
-
# 43_112_609,
|
|
872
|
-
# 57_885_161,
|
|
873
|
-
## 74_207_281,
|
|
874
|
-
## 77_232_917,
|
|
875
|
-
## 82_589_933
|
|
876
|
-
]
|
|
877
|
-
for x in perfect_number_index:
|
|
878
|
-
# a perfect number have a form of (2**(n-1))*((2**n)-1)
|
|
879
|
-
perfect_number = (2 ** (x - 1)) * ((2**x) - 1)
|
|
880
|
-
if number < perfect_number: # type: ignore
|
|
881
|
-
return False
|
|
882
|
-
elif number == perfect_number: # type: ignore
|
|
883
|
-
return True
|
|
884
|
-
|
|
885
|
-
# Manual way when above method not working
|
|
886
|
-
# sum
|
|
887
|
-
s = 1
|
|
888
|
-
# add all divisors
|
|
889
|
-
i = 2
|
|
890
|
-
while i * i <= number:
|
|
891
|
-
if number % i == 0:
|
|
892
|
-
s += +i + number / i # type: ignore
|
|
893
|
-
i += 1
|
|
894
|
-
# s == number -> perfect
|
|
895
|
-
return True if s == number and number != 1 else False
|
|
896
|
-
|
|
897
|
-
def is_narcissistic(self) -> bool:
|
|
898
|
-
"""
|
|
899
|
-
Check if a narcissistic number
|
|
900
|
-
|
|
901
|
-
In number theory, a narcissistic number
|
|
902
|
-
(also known as a pluperfect digital invariant (PPDI),
|
|
903
|
-
an Armstrong number (after Michael F. Armstrong)
|
|
904
|
-
or a plus perfect number) in a given number base ``b``
|
|
905
|
-
is a number that is the sum of its own digits
|
|
906
|
-
each raised to the power of the number of digits.
|
|
907
|
-
|
|
908
|
-
Returns
|
|
909
|
-
-------
|
|
910
|
-
bool
|
|
911
|
-
| ``True`` if a narcissistic number
|
|
912
|
-
| ``False`` if not a narcissistic number
|
|
913
|
-
"""
|
|
914
|
-
try:
|
|
915
|
-
check = sum([int(x) ** len(str(self)) for x in str(self)])
|
|
916
|
-
res = int(self) == check
|
|
917
|
-
return res # type: ignore
|
|
918
|
-
except Exception:
|
|
919
|
-
return False
|
|
920
|
-
|
|
921
|
-
def reverse(self) -> Self:
|
|
922
|
-
"""
|
|
923
|
-
Reverse a number. Reverse ``abs(number)`` if ``number < 0``
|
|
924
|
-
|
|
925
|
-
Returns
|
|
926
|
-
-------
|
|
927
|
-
IntNumber
|
|
928
|
-
Reversed number
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
Example:
|
|
932
|
-
--------
|
|
933
|
-
>>> test = IntNumber(102)
|
|
934
|
-
>>> test.reverse()
|
|
935
|
-
201
|
|
936
|
-
"""
|
|
937
|
-
number = int(self)
|
|
938
|
-
if number <= 1:
|
|
939
|
-
number *= -1
|
|
940
|
-
return self.__class__(str(number)[::-1])
|
|
941
|
-
|
|
942
|
-
def is_palindromic(self) -> bool:
|
|
943
|
-
"""
|
|
944
|
-
A palindromic number (also known as a numeral palindrome
|
|
945
|
-
or a numeric palindrome) is a number (such as ``16461``)
|
|
946
|
-
that remains the same when its digits are reversed.
|
|
947
|
-
|
|
948
|
-
Returns
|
|
949
|
-
-------
|
|
950
|
-
bool
|
|
951
|
-
| ``True`` if a palindromic number
|
|
952
|
-
| ``False`` if not a palindromic number
|
|
953
|
-
"""
|
|
954
|
-
return self == self.reverse()
|
|
955
|
-
|
|
956
|
-
def is_palindromic_prime(self) -> bool:
|
|
957
|
-
"""
|
|
958
|
-
A palindormic prime is a number which is both palindromic and prime
|
|
959
|
-
|
|
960
|
-
Returns
|
|
961
|
-
-------
|
|
962
|
-
bool
|
|
963
|
-
| ``True`` if a palindormic prime number
|
|
964
|
-
| ``False`` if not a palindormic prime number
|
|
965
|
-
"""
|
|
966
|
-
return self.is_palindromic() and self.is_prime()
|
|
967
|
-
|
|
968
|
-
# calculation stuff
|
|
969
|
-
@versionchanged(version="4.0.0", reason="Update")
|
|
970
|
-
def lcm(self, with_number: int) -> Self:
|
|
971
|
-
"""
|
|
972
|
-
Least common multiple of ``self`` and ``with_number``
|
|
973
|
-
|
|
974
|
-
Parameters
|
|
975
|
-
----------
|
|
976
|
-
with_number : int
|
|
977
|
-
The number that want to find LCM with
|
|
978
|
-
|
|
979
|
-
Returns
|
|
980
|
-
-------
|
|
981
|
-
IntNumber
|
|
982
|
-
Least common multiple
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
Example:
|
|
986
|
-
--------
|
|
987
|
-
>>> test = IntNumber(102)
|
|
988
|
-
>>> test.lcm(5)
|
|
989
|
-
510
|
|
990
|
-
"""
|
|
991
|
-
return self.__class__(math.lcm(self, with_number))
|
|
992
|
-
|
|
993
|
-
@versionchanged(version="3.3.0", reason="Fix bug")
|
|
994
|
-
def gcd(self, with_number: int) -> Self:
|
|
995
|
-
"""
|
|
996
|
-
Greatest common divisor of ``self`` and ``with_number``
|
|
997
|
-
|
|
998
|
-
Parameters
|
|
999
|
-
----------
|
|
1000
|
-
with_number : int
|
|
1001
|
-
The number that want to find GCD with
|
|
1002
|
-
|
|
1003
|
-
Returns
|
|
1004
|
-
-------
|
|
1005
|
-
IntNumber
|
|
1006
|
-
Greatest common divisor
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
Example:
|
|
1010
|
-
--------
|
|
1011
|
-
>>> test = IntNumber(1024)
|
|
1012
|
-
>>> test.gcd(8)
|
|
1013
|
-
8
|
|
1014
|
-
"""
|
|
1015
|
-
return self.__class__(math.gcd(self, with_number))
|
|
1016
|
-
|
|
1017
|
-
def add_to_one_digit(self, master_number: bool = False) -> Self:
|
|
1018
|
-
"""
|
|
1019
|
-
Convert ``self`` into 1-digit number
|
|
1020
|
-
by adding all of the digits together
|
|
1021
|
-
|
|
1022
|
-
Parameters
|
|
1023
|
-
----------
|
|
1024
|
-
master_number : bool
|
|
1025
|
-
| Break when sum = ``22`` or ``11`` (numerology)
|
|
1026
|
-
| (Default: ``False``)
|
|
1027
|
-
|
|
1028
|
-
Returns
|
|
1029
|
-
-------
|
|
1030
|
-
IntNumber
|
|
1031
|
-
IntNumber
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
Example:
|
|
1035
|
-
--------
|
|
1036
|
-
>>> test = IntNumber(119)
|
|
1037
|
-
>>> test.add_to_one_digit()
|
|
1038
|
-
2
|
|
1039
|
-
|
|
1040
|
-
>>> test = IntNumber(119)
|
|
1041
|
-
>>> test.add_to_one_digit(master_number=True)
|
|
1042
|
-
11
|
|
1043
|
-
"""
|
|
1044
|
-
number = int(self)
|
|
1045
|
-
if number < 0:
|
|
1046
|
-
number *= -1
|
|
1047
|
-
logger.debug(f"Current number: {number}")
|
|
1048
|
-
while len(str(number)) != 1:
|
|
1049
|
-
number = sum(map(int, str(number)))
|
|
1050
|
-
if master_number:
|
|
1051
|
-
if number == 22 or number == 11:
|
|
1052
|
-
break # Master number
|
|
1053
|
-
logger.debug(f"Sum after loop: {number}")
|
|
1054
|
-
return self.__class__(number)
|
|
1055
|
-
|
|
1056
|
-
def divisible_list(self, short_form: bool = True) -> list[int]:
|
|
1057
|
-
"""
|
|
1058
|
-
A list of divisible number
|
|
1059
|
-
|
|
1060
|
-
Parameters
|
|
1061
|
-
----------
|
|
1062
|
-
short_form : bool
|
|
1063
|
-
| Show divisible list in short form
|
|
1064
|
-
| Normal example: ``[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]``
|
|
1065
|
-
| Short form example: ``[1, 2, 4, 8, ...,128, 256, 512, 1024] [Len: 11]``
|
|
1066
|
-
| (Default: ``True``)
|
|
1067
|
-
|
|
1068
|
-
Returns
|
|
1069
|
-
-------
|
|
1070
|
-
list[int]
|
|
1071
|
-
A list of divisible number
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
Example:
|
|
1075
|
-
--------
|
|
1076
|
-
>>> test = IntNumber(1024)
|
|
1077
|
-
>>> test.divisible_list(short_form=False)
|
|
1078
|
-
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]
|
|
1079
|
-
"""
|
|
1080
|
-
|
|
1081
|
-
if self <= 1:
|
|
1082
|
-
return [1]
|
|
1083
|
-
divi_list = [x for x in range(1, int(self / 2) + 1) if self % x == 0] + [self]
|
|
1084
|
-
|
|
1085
|
-
if short_form:
|
|
1086
|
-
return divi_list
|
|
1087
|
-
# return ListREPR(divi_list) ## FIX LATER
|
|
1088
|
-
return divi_list
|
|
1089
|
-
|
|
1090
|
-
def prime_factor(self, short_form: bool = True) -> Union[list[int], list[Pow]]:
|
|
1091
|
-
"""
|
|
1092
|
-
Prime factor
|
|
1093
|
-
|
|
1094
|
-
Parameters
|
|
1095
|
-
----------
|
|
1096
|
-
short_form : bool
|
|
1097
|
-
| Show prime list in short form
|
|
1098
|
-
| Normal example: ``[2, 2, 2, 3, 3]``
|
|
1099
|
-
| Short form example: ``[2^3, 3^2]``
|
|
1100
|
-
| (Default: ``True``)
|
|
1101
|
-
|
|
1102
|
-
Returns
|
|
1103
|
-
-------
|
|
1104
|
-
list[int] | list[Pow]
|
|
1105
|
-
| List of prime number that when multiplied together == ``self``
|
|
1106
|
-
| list[int]: Long form
|
|
1107
|
-
| list[Pow]: Short form
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
Example:
|
|
1111
|
-
--------
|
|
1112
|
-
>>> test = IntNumber(1024)
|
|
1113
|
-
>>> test.prime_factor()
|
|
1114
|
-
[2^10]
|
|
1115
|
-
|
|
1116
|
-
>>> test = IntNumber(1024)
|
|
1117
|
-
>>> test.prime_factor(short_form=False)
|
|
1118
|
-
[2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
|
|
1119
|
-
"""
|
|
1120
|
-
# Generate list
|
|
1121
|
-
factors = []
|
|
1122
|
-
divisor = 2
|
|
1123
|
-
number = int(self)
|
|
1124
|
-
if number <= 1:
|
|
1125
|
-
return [number]
|
|
1126
|
-
while divisor <= number:
|
|
1127
|
-
if number % divisor == 0:
|
|
1128
|
-
factors.append(divisor)
|
|
1129
|
-
number //= divisor # number = number // divisor
|
|
1130
|
-
else:
|
|
1131
|
-
divisor += 1
|
|
1132
|
-
|
|
1133
|
-
# Output
|
|
1134
|
-
if short_form:
|
|
1135
|
-
temp = dict(Counter(factors))
|
|
1136
|
-
return [Pow(k, v) for k, v in temp.items()]
|
|
1137
|
-
return factors
|
|
1138
|
-
|
|
1139
|
-
# analyze
|
|
1140
|
-
def analyze(self, short_form: bool = True) -> dict[str, dict[str, Any]]:
|
|
1141
|
-
"""
|
|
1142
|
-
Analyze the number with almost all ``IntNumber`` method
|
|
1143
|
-
|
|
1144
|
-
Parameters
|
|
1145
|
-
----------
|
|
1146
|
-
short_form : bool
|
|
1147
|
-
| Enable short form for some items
|
|
1148
|
-
| (Default: ``True``)
|
|
1149
|
-
|
|
1150
|
-
Returns
|
|
1151
|
-
-------
|
|
1152
|
-
dict[str, dict[str, Any]]
|
|
1153
|
-
Detailed analysis
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
Example:
|
|
1157
|
-
--------
|
|
1158
|
-
>>> test = IntNumber(1024)
|
|
1159
|
-
>>> test.analyze()
|
|
1160
|
-
{
|
|
1161
|
-
'summary': {'number': 1024, 'length': 4, 'even': True, 'prime factor': [2^10], 'divisible': [1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024]},
|
|
1162
|
-
'convert': {'binary': '10000000000', 'octa': '2000', 'hex': '400', 'reverse': 4201, 'add to one': 7},
|
|
1163
|
-
'characteristic': {'prime': False, 'twisted prime': False, 'perfect': False, 'narcissistic': False, 'palindromic': False, 'palindromic prime': False}
|
|
1164
|
-
}
|
|
1165
|
-
"""
|
|
1166
|
-
output = {
|
|
1167
|
-
"summary": {
|
|
1168
|
-
"number": self,
|
|
1169
|
-
"length": len(str(self)),
|
|
1170
|
-
"even": self.is_even(),
|
|
1171
|
-
"prime factor": self.prime_factor(short_form=short_form),
|
|
1172
|
-
"divisible": self.divisible_list(short_form=short_form),
|
|
1173
|
-
},
|
|
1174
|
-
"convert": {
|
|
1175
|
-
"binary": bin(self)[2:],
|
|
1176
|
-
"octa": oct(self)[2:],
|
|
1177
|
-
"hex": hex(self)[2:],
|
|
1178
|
-
# "hash": hash(self),
|
|
1179
|
-
"reverse": self.reverse(),
|
|
1180
|
-
"add to one": self.add_to_one_digit(),
|
|
1181
|
-
},
|
|
1182
|
-
}
|
|
1183
|
-
characteristic = {
|
|
1184
|
-
"prime": self.is_prime(),
|
|
1185
|
-
"twisted prime": self.is_twisted_prime(),
|
|
1186
|
-
"perfect": self.is_perfect(),
|
|
1187
|
-
"narcissistic": self.is_narcissistic(),
|
|
1188
|
-
"palindromic": self.is_palindromic(),
|
|
1189
|
-
"palindromic prime": self.is_palindromic_prime(),
|
|
1190
|
-
}
|
|
1191
|
-
if short_form:
|
|
1192
|
-
characteristic = DictBoolTrue(characteristic)
|
|
1193
|
-
|
|
1194
|
-
output["characteristic"] = characteristic
|
|
1195
|
-
return output # type: ignore
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
# MARK: ListExt
|
|
1199
|
-
class ListExt(list):
|
|
1200
|
-
"""
|
|
1201
|
-
``list`` extension
|
|
1202
|
-
"""
|
|
1203
|
-
|
|
1204
|
-
def stringify(self) -> Self:
|
|
1205
|
-
"""
|
|
1206
|
-
Convert all item in ``list`` into string
|
|
1207
|
-
|
|
1208
|
-
Returns
|
|
1209
|
-
-------
|
|
1210
|
-
ListExt
|
|
1211
|
-
A list with all items with type <str`>
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
Example:
|
|
1215
|
-
--------
|
|
1216
|
-
>>> test = ListExt([1, 1, 1, 2, 2, 3])
|
|
1217
|
-
>>> test.stringify()
|
|
1218
|
-
['1', '1', '1', '2', '2', '3']
|
|
1219
|
-
"""
|
|
1220
|
-
return self.__class__(map(str, self))
|
|
1221
|
-
|
|
1222
|
-
def head(self, number_of_items: int = 5) -> list:
|
|
1223
|
-
"""
|
|
1224
|
-
Show first ``number_of_items`` items in ``ListExt``
|
|
1225
|
-
|
|
1226
|
-
Parameters
|
|
1227
|
-
----------
|
|
1228
|
-
number_of_items : int
|
|
1229
|
-
| Number of items to shows at once
|
|
1230
|
-
| (Default: ``5``)
|
|
1231
|
-
|
|
1232
|
-
Returns
|
|
1233
|
-
-------
|
|
1234
|
-
list
|
|
1235
|
-
Filtered list
|
|
1236
|
-
"""
|
|
1237
|
-
number_of_items = int(
|
|
1238
|
-
set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1239
|
-
)
|
|
1240
|
-
return self[:number_of_items]
|
|
1241
|
-
|
|
1242
|
-
def tail(self, number_of_items: int = 5) -> list:
|
|
1243
|
-
"""
|
|
1244
|
-
Show last ``number_of_items`` items in ``ListExt``
|
|
1245
|
-
|
|
1246
|
-
Parameters
|
|
1247
|
-
----------
|
|
1248
|
-
number_of_items : int
|
|
1249
|
-
| Number of items to shows at once
|
|
1250
|
-
| (Default: ``5``)
|
|
1251
|
-
|
|
1252
|
-
Returns
|
|
1253
|
-
-------
|
|
1254
|
-
list
|
|
1255
|
-
Filtered list
|
|
1256
|
-
"""
|
|
1257
|
-
number_of_items = int(
|
|
1258
|
-
set_min_max(number_of_items, min_value=0, max_value=len(self))
|
|
1259
|
-
)
|
|
1260
|
-
return self[::-1][:number_of_items][::-1]
|
|
1261
|
-
|
|
1262
|
-
def sorts(self, reverse: bool = False) -> Self:
|
|
1263
|
-
"""
|
|
1264
|
-
Sort all items (with different type) in ``list``
|
|
1265
|
-
|
|
1266
|
-
Parameters
|
|
1267
|
-
----------
|
|
1268
|
-
reverse : bool
|
|
1269
|
-
| if ``True`` then sort in descending order
|
|
1270
|
-
| if ``False`` then sort in ascending order
|
|
1271
|
-
| (Default: ``False``)
|
|
1272
|
-
|
|
1273
|
-
Returns
|
|
1274
|
-
-------
|
|
1275
|
-
ListExt
|
|
1276
|
-
A sorted list
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
Example:
|
|
1280
|
-
--------
|
|
1281
|
-
>>> test = ListExt([9, "abc", 3.5, "aaa", 1, 1.4])
|
|
1282
|
-
>>> test.sorts()
|
|
1283
|
-
[1, 9, 'aaa', 'abc', 1.4, 3.5]
|
|
1284
|
-
"""
|
|
1285
|
-
lst = self.copy()
|
|
1286
|
-
type_weights: dict = {}
|
|
1287
|
-
for x in lst:
|
|
1288
|
-
if type(x) not in type_weights:
|
|
1289
|
-
type_weights[type(x)] = len(type_weights)
|
|
1290
|
-
logger.debug(f"Type weight: {type_weights}")
|
|
1291
|
-
|
|
1292
|
-
output = sorted(
|
|
1293
|
-
lst, key=lambda x: (type_weights[type(x)], str(x)), reverse=reverse
|
|
1294
|
-
)
|
|
1295
|
-
|
|
1296
|
-
logger.debug(output)
|
|
1297
|
-
return self.__class__(output)
|
|
1298
|
-
|
|
1299
|
-
def freq(
|
|
1300
|
-
self,
|
|
1301
|
-
sort: bool = False,
|
|
1302
|
-
num_of_first_char: int | None = None,
|
|
1303
|
-
appear_increment: bool = False,
|
|
1304
|
-
) -> Union[dict, list[int]]:
|
|
1305
|
-
"""
|
|
1306
|
-
Find frequency of each item in list
|
|
1307
|
-
|
|
1308
|
-
Parameters
|
|
1309
|
-
----------
|
|
1310
|
-
sort : bool
|
|
1311
|
-
| if ``True``: Sorts the output in ascending order
|
|
1312
|
-
| if ``False``: No sort
|
|
1313
|
-
|
|
1314
|
-
num_of_first_char : int | None
|
|
1315
|
-
| Number of first character taken into account to sort
|
|
1316
|
-
| (Default: ``None``)
|
|
1317
|
-
| (num_of_first_char = ``1``: first character in each item)
|
|
1318
|
-
|
|
1319
|
-
appear_increment : bool
|
|
1320
|
-
| return incremental index list of each item when sort
|
|
1321
|
-
| (Default: ``False``)
|
|
1322
|
-
|
|
1323
|
-
Returns
|
|
1324
|
-
-------
|
|
1325
|
-
dict
|
|
1326
|
-
A dict that show frequency
|
|
1327
|
-
|
|
1328
|
-
list[int]
|
|
1329
|
-
Incremental index list
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
Example:
|
|
1333
|
-
--------
|
|
1334
|
-
>>> test = ListExt([1, 1, 2, 3, 5, 5])
|
|
1335
|
-
>>> test.freq()
|
|
1336
|
-
{1: 2, 2: 1, 3: 1, 5: 2}
|
|
1337
|
-
|
|
1338
|
-
>>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
|
|
1339
|
-
>>> test.freq(appear_increment=True)
|
|
1340
|
-
[2, 3, 5, 6, 7, 8]
|
|
1341
|
-
"""
|
|
1342
|
-
|
|
1343
|
-
if sort:
|
|
1344
|
-
data = self.sorts().copy()
|
|
1345
|
-
else:
|
|
1346
|
-
data = self.copy()
|
|
1347
|
-
|
|
1348
|
-
if num_of_first_char is None:
|
|
1349
|
-
temp = Counter(data)
|
|
1350
|
-
else:
|
|
1351
|
-
max_char: int = min([len(str(x)) for x in data])
|
|
1352
|
-
logger.debug(f"Max character: {max_char}")
|
|
1353
|
-
if num_of_first_char not in range(1, max_char):
|
|
1354
|
-
logger.debug(f"Not in {range(1, max_char)}. Using default value...")
|
|
1355
|
-
temp = Counter(data)
|
|
1356
|
-
else:
|
|
1357
|
-
logger.debug(f"Freq of first {num_of_first_char} char")
|
|
1358
|
-
temp = Counter([str(x)[:num_of_first_char] for x in data])
|
|
1359
|
-
|
|
1360
|
-
try:
|
|
1361
|
-
times_appear = dict(sorted(temp.items()))
|
|
1362
|
-
except Exception:
|
|
1363
|
-
times_appear = dict(self.__class__(temp.items()).sorts())
|
|
1364
|
-
logger.debug(times_appear)
|
|
1365
|
-
|
|
1366
|
-
if appear_increment:
|
|
1367
|
-
times_appear_increment: list[int] = list(
|
|
1368
|
-
accumulate(times_appear.values(), operator.add)
|
|
1369
|
-
)
|
|
1370
|
-
logger.debug(times_appear_increment)
|
|
1371
|
-
return times_appear_increment
|
|
1372
|
-
else:
|
|
1373
|
-
return times_appear
|
|
1374
|
-
|
|
1375
|
-
def slice_points(self, points: list) -> list[list]:
|
|
1376
|
-
"""
|
|
1377
|
-
Slices a list at specific indices into constituent lists.
|
|
1378
|
-
|
|
1379
|
-
Parameters
|
|
1380
|
-
----------
|
|
1381
|
-
points : list
|
|
1382
|
-
List of indices to slice
|
|
1383
|
-
|
|
1384
|
-
Returns
|
|
1385
|
-
-------
|
|
1386
|
-
list[list]
|
|
1387
|
-
Sliced list
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
Example:
|
|
1391
|
-
--------
|
|
1392
|
-
>>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
|
|
1393
|
-
>>> test.slice_points([2, 5])
|
|
1394
|
-
[[1, 1], [2, 3, 3], [4, 5, 6]]
|
|
1395
|
-
"""
|
|
1396
|
-
points.append(len(self))
|
|
1397
|
-
data = self.copy()
|
|
1398
|
-
# return [data[points[i]:points[i+1]] for i in range(len(points)-1)]
|
|
1399
|
-
return [data[i1:i2] for i1, i2 in zip([0] + points[:-1], points)]
|
|
1400
|
-
|
|
1401
|
-
def pick_one(self) -> Any:
|
|
1402
|
-
"""
|
|
1403
|
-
Pick one random items from ``list``
|
|
1404
|
-
|
|
1405
|
-
Returns
|
|
1406
|
-
-------
|
|
1407
|
-
Any
|
|
1408
|
-
Random value
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
Example:
|
|
1412
|
-
--------
|
|
1413
|
-
>>> test = ListExt(["foo", "bar"])
|
|
1414
|
-
>>> test.pick_one()
|
|
1415
|
-
'bar'
|
|
1416
|
-
"""
|
|
1417
|
-
if len(self) != 0:
|
|
1418
|
-
out = random.choice(self)
|
|
1419
|
-
logger.debug(out)
|
|
1420
|
-
return out
|
|
1421
|
-
else:
|
|
1422
|
-
logger.debug("List empty!")
|
|
1423
|
-
raise IndexError("List empty!")
|
|
1424
|
-
|
|
1425
|
-
def get_random(self, number_of_items: int = 5) -> list:
|
|
1426
|
-
"""
|
|
1427
|
-
Get ``number_of_items`` random items in ``ListExt``
|
|
1428
|
-
|
|
1429
|
-
Parameters
|
|
1430
|
-
----------
|
|
1431
|
-
number_of_items : int
|
|
1432
|
-
| Number random of items
|
|
1433
|
-
| (Default: ``5``)
|
|
1434
|
-
|
|
1435
|
-
Returns
|
|
1436
|
-
-------
|
|
1437
|
-
list
|
|
1438
|
-
Filtered list
|
|
1439
|
-
"""
|
|
1440
|
-
return [self.pick_one() for _ in range(number_of_items)]
|
|
1441
|
-
|
|
1442
|
-
def len_items(self) -> Self:
|
|
1443
|
-
"""
|
|
1444
|
-
``len()`` for every item in ``list[str]``
|
|
1445
|
-
|
|
1446
|
-
Returns
|
|
1447
|
-
-------
|
|
1448
|
-
ListExt
|
|
1449
|
-
List of ``len()``'ed value
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
Example:
|
|
1453
|
-
--------
|
|
1454
|
-
>>> test = ListExt(["foo", "bar", "pizza"])
|
|
1455
|
-
>>> test.len_items()
|
|
1456
|
-
[3, 3, 5]
|
|
1457
|
-
"""
|
|
1458
|
-
out = self.__class__([len(str(x)) for x in self])
|
|
1459
|
-
# out = ListExt(map(lambda x: len(str(x)), self))
|
|
1460
|
-
logger.debug(out)
|
|
1461
|
-
return out
|
|
1462
|
-
|
|
1463
|
-
def mean_len(self) -> float:
|
|
1464
|
-
"""
|
|
1465
|
-
Average length of every item
|
|
1466
|
-
|
|
1467
|
-
Returns
|
|
1468
|
-
-------
|
|
1469
|
-
float
|
|
1470
|
-
Average length
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
Example:
|
|
1474
|
-
--------
|
|
1475
|
-
>>> test = ListExt(["foo", "bar", "pizza"])
|
|
1476
|
-
>>> test.mean_len()
|
|
1477
|
-
3.6666666666666665
|
|
1478
|
-
"""
|
|
1479
|
-
out = sum(self.len_items()) / len(self)
|
|
1480
|
-
logger.debug(out)
|
|
1481
|
-
return out
|
|
1482
|
-
|
|
1483
|
-
def apply(self, func: Callable) -> Self:
|
|
1484
|
-
"""
|
|
1485
|
-
Apply function to each entry
|
|
1486
|
-
|
|
1487
|
-
Parameters
|
|
1488
|
-
----------
|
|
1489
|
-
func : Callable
|
|
1490
|
-
Callable function
|
|
1491
|
-
|
|
1492
|
-
Returns
|
|
1493
|
-
-------
|
|
1494
|
-
ListExt
|
|
1495
|
-
ListExt
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
Example:
|
|
1499
|
-
--------
|
|
1500
|
-
>>> test = ListExt([1, 2, 3])
|
|
1501
|
-
>>> test.apply(str)
|
|
1502
|
-
['1', '2', '3']
|
|
1503
|
-
"""
|
|
1504
|
-
# return __class__(func(x) for x in self)
|
|
1505
|
-
return self.__class__(map(func, self))
|
|
1506
|
-
|
|
1507
|
-
def unique(self) -> Self:
|
|
1508
|
-
"""
|
|
1509
|
-
Remove duplicates
|
|
1510
|
-
|
|
1511
|
-
Returns
|
|
1512
|
-
-------
|
|
1513
|
-
ListExt
|
|
1514
|
-
Duplicates removed list
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
Example:
|
|
1518
|
-
--------
|
|
1519
|
-
>>> test = ListExt([1, 1, 1, 2, 2, 3])
|
|
1520
|
-
>>> test.unique()
|
|
1521
|
-
[1, 2, 3]
|
|
1522
|
-
"""
|
|
1523
|
-
return self.__class__(set(self))
|
|
1524
|
-
|
|
1525
|
-
def group_by_unique(self) -> Self:
|
|
1526
|
-
"""
|
|
1527
|
-
Group duplicated elements into list
|
|
1528
|
-
|
|
1529
|
-
Returns
|
|
1530
|
-
-------
|
|
1531
|
-
ListExt
|
|
1532
|
-
Grouped value
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
Example:
|
|
1536
|
-
--------
|
|
1537
|
-
>>> test = ListExt([1, 2, 3, 1, 3, 3, 2])
|
|
1538
|
-
>>> test.group_by_unique()
|
|
1539
|
-
[[1, 1], [2, 2], [3, 3, 3]]
|
|
1540
|
-
"""
|
|
1541
|
-
# Old
|
|
1542
|
-
# out = self.sorts().slice_points(self.freq(appear_increment=True))
|
|
1543
|
-
# return __class__(out[:-1])
|
|
1544
|
-
|
|
1545
|
-
# New
|
|
1546
|
-
temp = groupby(self.sorts())
|
|
1547
|
-
return self.__class__([list(g) for _, g in temp])
|
|
1548
|
-
|
|
1549
|
-
@staticmethod
|
|
1550
|
-
def _group_by_unique(iterable: list) -> list[list]:
|
|
1551
|
-
"""
|
|
1552
|
-
Static method for ``group_by_unique``
|
|
1553
|
-
"""
|
|
1554
|
-
return list([list(g) for _, g in groupby(iterable)])
|
|
1555
|
-
|
|
1556
|
-
def group_by_pair_value(self, max_loop: int = 3) -> list[list]:
|
|
1557
|
-
"""
|
|
1558
|
-
Assume each ``list`` in ``list`` is a pair value,
|
|
1559
|
-
returns a ``list`` contain all paired value
|
|
1560
|
-
|
|
1561
|
-
Parameters
|
|
1562
|
-
----------
|
|
1563
|
-
max_loop : int
|
|
1564
|
-
Times to run functions (minimum: ``3``)
|
|
1565
|
-
|
|
1566
|
-
Returns
|
|
1567
|
-
-------
|
|
1568
|
-
list[list]
|
|
1569
|
-
Grouped value
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
Example:
|
|
1573
|
-
--------
|
|
1574
|
-
>>> test = ListExt([[1, 2], [2, 3], [4, 3], [5, 6]])
|
|
1575
|
-
>>> test.group_by_pair_value()
|
|
1576
|
-
[[1, 2, 3, 4], [5, 6]]
|
|
1577
|
-
|
|
1578
|
-
>>> test = ListExt([[8, 3], [4, 6], [6, 3], [5, 2], [7, 2]])
|
|
1579
|
-
>>> test.group_by_pair_value()
|
|
1580
|
-
[[8, 3, 4, 6], [2, 5, 7]]
|
|
1581
|
-
|
|
1582
|
-
>>> test = ListExt([["a", 4], ["b", 4], [5, "c"]])
|
|
1583
|
-
>>> test.group_by_pair_value()
|
|
1584
|
-
[['a', 4, 'b'], ['c', 5]]
|
|
1585
|
-
"""
|
|
1586
|
-
|
|
1587
|
-
iter = self.copy()
|
|
1588
|
-
|
|
1589
|
-
# Init loop
|
|
1590
|
-
for _ in range(int(set_min(max_loop, min_value=3))):
|
|
1591
|
-
temp: dict[Any, list] = {}
|
|
1592
|
-
# Make dict{key: all `item` that contains `key`}
|
|
1593
|
-
for item in iter:
|
|
1594
|
-
for x in item:
|
|
1595
|
-
if temp.get(x, None) is None:
|
|
1596
|
-
temp[x] = [item]
|
|
1597
|
-
else:
|
|
1598
|
-
temp[x].append(item)
|
|
1599
|
-
|
|
1600
|
-
# Flatten dict.values
|
|
1601
|
-
for k, v in temp.items():
|
|
1602
|
-
temp[k] = list(set(chain(*v)))
|
|
1603
|
-
|
|
1604
|
-
iter = list(temp.values())
|
|
1605
|
-
|
|
1606
|
-
return list(x for x, _ in groupby(iter))
|
|
1607
|
-
|
|
1608
|
-
def flatten(self) -> Self:
|
|
1609
|
-
"""
|
|
1610
|
-
Flatten the list
|
|
1611
|
-
|
|
1612
|
-
Returns
|
|
1613
|
-
-------
|
|
1614
|
-
ListExt
|
|
1615
|
-
Flattened list
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
Example:
|
|
1619
|
-
--------
|
|
1620
|
-
>>> test = ListExt([["test"], ["test", "test"], ["test"]])
|
|
1621
|
-
>>> test.flatten()
|
|
1622
|
-
['test', 'test', 'test', 'test']
|
|
1623
|
-
"""
|
|
1624
|
-
try:
|
|
1625
|
-
return self.__class__(chain(*self))
|
|
1626
|
-
except Exception:
|
|
1627
|
-
temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
|
|
1628
|
-
return self.__class__(chain(*temp))
|
|
1629
|
-
|
|
1630
|
-
def numbering(self, start: int = 0) -> Self:
|
|
1631
|
-
"""
|
|
1632
|
-
Number the item in list
|
|
1633
|
-
(``enumerate`` wrapper)
|
|
1634
|
-
|
|
1635
|
-
Parameters
|
|
1636
|
-
----------
|
|
1637
|
-
start : int
|
|
1638
|
-
Start from which number
|
|
1639
|
-
(Default: ``0``)
|
|
1640
|
-
|
|
1641
|
-
Returns
|
|
1642
|
-
-------
|
|
1643
|
-
ListExt
|
|
1644
|
-
Counted list
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
Example:
|
|
1648
|
-
--------
|
|
1649
|
-
>>> test = ListExt([9, 9, 9])
|
|
1650
|
-
>>> test.numbering()
|
|
1651
|
-
[(0, 9), (1, 9), (2, 9)]
|
|
1652
|
-
"""
|
|
1653
|
-
start = int(set_min(start, min_value=0))
|
|
1654
|
-
return self.__class__(enumerate(self, start=start))
|
|
1655
|
-
|
|
1656
|
-
@staticmethod
|
|
1657
|
-
def _numbering(iterable: list, start: int = 0) -> list[tuple[int, Any]]:
|
|
1658
|
-
"""
|
|
1659
|
-
Static method for ``numbering``
|
|
1660
|
-
"""
|
|
1661
|
-
start = int(set_min(start, min_value=0))
|
|
1662
|
-
return list(enumerate(iterable, start=start))
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
# MARK: DictExt
|
|
1666
|
-
class DictExt(dict):
|
|
1667
|
-
"""
|
|
1668
|
-
``dict`` extension
|
|
1669
|
-
"""
|
|
1670
|
-
|
|
1671
|
-
@versionchanged(
|
|
1672
|
-
version="3.3.0",
|
|
1673
|
-
reason="Change function output; Before: <dict>, Now: DictAnalyzeResult",
|
|
1674
|
-
)
|
|
1675
|
-
def analyze(self) -> DictAnalyzeResult:
|
|
1676
|
-
"""
|
|
1677
|
-
Analyze all the key values (``int``, ``float``)
|
|
1678
|
-
in ``dict`` then return highest/lowest index
|
|
1679
|
-
|
|
1680
|
-
Returns
|
|
1681
|
-
-------
|
|
1682
|
-
dict
|
|
1683
|
-
Analyzed data
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
Example:
|
|
1687
|
-
--------
|
|
1688
|
-
>>> test = DictExt({
|
|
1689
|
-
>>> "abc": 9,
|
|
1690
|
-
>>> "def": 9,
|
|
1691
|
-
>>> "ghi": 8,
|
|
1692
|
-
>>> "jkl": 1,
|
|
1693
|
-
>>> "mno": 1
|
|
1694
|
-
>>> })
|
|
1695
|
-
>>> test.analyze()
|
|
1696
|
-
DictAnalyzeResult(max_value=9, min_value=1, max_list=[('abc', 9), ('def', 9)], min_list=[('jkl', 1), ('mno', 1)])
|
|
1697
|
-
"""
|
|
1698
|
-
try:
|
|
1699
|
-
dct: dict = self.copy()
|
|
1700
|
-
|
|
1701
|
-
max_val: int | float = max(list(dct.values()))
|
|
1702
|
-
min_val: int | float = min(list(dct.values()))
|
|
1703
|
-
max_list = []
|
|
1704
|
-
min_list = []
|
|
1705
|
-
|
|
1706
|
-
for k, v in dct.items():
|
|
1707
|
-
if v == max_val:
|
|
1708
|
-
max_list.append((k, v))
|
|
1709
|
-
if v == min_val:
|
|
1710
|
-
min_list.append((k, v))
|
|
1711
|
-
|
|
1712
|
-
# output = {
|
|
1713
|
-
# "max_value": max_val,
|
|
1714
|
-
# "min_value": min_val,
|
|
1715
|
-
# "max": max_list,
|
|
1716
|
-
# "min": min_list,
|
|
1717
|
-
# }
|
|
1718
|
-
|
|
1719
|
-
# logger.debug(output)
|
|
1720
|
-
# return output
|
|
1721
|
-
return DictAnalyzeResult(max_val, min_val, max_list, min_list)
|
|
1722
|
-
|
|
1723
|
-
except Exception:
|
|
1724
|
-
err_msg = "Value must be int or float"
|
|
1725
|
-
logger.error(err_msg)
|
|
1726
|
-
raise ValueError(err_msg) # noqa: B904
|
|
1727
|
-
|
|
1728
|
-
def swap_items(self) -> Self:
|
|
1729
|
-
"""
|
|
1730
|
-
Swap ``dict.keys()`` with ``dict.values()``
|
|
1731
|
-
|
|
1732
|
-
Returns
|
|
1733
|
-
-------
|
|
1734
|
-
DictExt
|
|
1735
|
-
Swapped dict
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
Example:
|
|
1739
|
-
--------
|
|
1740
|
-
>>> test = DictExt({"abc": 9})
|
|
1741
|
-
>>> test.swap_items()
|
|
1742
|
-
{9: 'abc'}
|
|
1743
|
-
"""
|
|
1744
|
-
return self.__class__(zip(self.values(), self.keys()))
|
|
1745
|
-
|
|
1746
|
-
def apply(self, func: Callable, apply_to_value: bool = True) -> Self:
|
|
1747
|
-
"""
|
|
1748
|
-
Apply function to ``DictExt.keys()`` or ``DictExt.values()``
|
|
1749
|
-
|
|
1750
|
-
Parameters
|
|
1751
|
-
----------
|
|
1752
|
-
func : Callable
|
|
1753
|
-
Callable function
|
|
1754
|
-
|
|
1755
|
-
apply_to_value : bool
|
|
1756
|
-
| ``True``: Apply ``func`` to ``DictExt.values()``
|
|
1757
|
-
| ``False``: Apply ``func`` to ``DictExt.keys()``
|
|
1758
|
-
|
|
1759
|
-
Returns
|
|
1760
|
-
-------
|
|
1761
|
-
DictExt
|
|
1762
|
-
DictExt
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
Example:
|
|
1766
|
-
--------
|
|
1767
|
-
>>> test = DictExt({"abc": 9})
|
|
1768
|
-
>>> test.apply(str)
|
|
1769
|
-
{'abc': '9'}
|
|
1770
|
-
"""
|
|
1771
|
-
if apply_to_value:
|
|
1772
|
-
k = self.keys()
|
|
1773
|
-
v = map(func, self.values())
|
|
1774
|
-
else:
|
|
1775
|
-
k = map(func, self.keys()) # type: ignore
|
|
1776
|
-
v = self.values() # type: ignore
|
|
1777
|
-
return self.__class__(zip(k, v))
|
|
1778
|
-
|
|
1779
|
-
@versionadded(version="3.4.0")
|
|
1780
|
-
def aggregate(
|
|
1781
|
-
self,
|
|
1782
|
-
other_dict: dict[Any, int | float],
|
|
1783
|
-
default_value: int | float = 0,
|
|
1784
|
-
) -> Self:
|
|
1785
|
-
"""Dict with value type int or float"""
|
|
1786
|
-
out = {
|
|
1787
|
-
k: self.get(k, default_value) + other_dict.get(k, default_value)
|
|
1788
|
-
for k in set(self | other_dict)
|
|
1789
|
-
}
|
|
1790
|
-
return self.__class__(out)
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
# Run
|
|
1794
|
-
###########################################################################
|
|
1795
|
-
if __name__ == "__main__":
|
|
1796
|
-
logger.setLevel(10)
|