absfuyu 5.0.1__py3-none-any.whl → 5.2.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.

Files changed (72) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +2 -2
  4. absfuyu/cli/color.py +30 -14
  5. absfuyu/cli/config_group.py +9 -2
  6. absfuyu/cli/do_group.py +13 -6
  7. absfuyu/cli/game_group.py +9 -2
  8. absfuyu/cli/tool_group.py +15 -9
  9. absfuyu/config/__init__.py +2 -2
  10. absfuyu/core/__init__.py +2 -2
  11. absfuyu/core/baseclass.py +448 -79
  12. absfuyu/core/baseclass2.py +2 -2
  13. absfuyu/core/decorator.py +70 -4
  14. absfuyu/core/docstring.py +43 -25
  15. absfuyu/core/dummy_cli.py +2 -2
  16. absfuyu/core/dummy_func.py +15 -4
  17. absfuyu/dxt/__init__.py +2 -2
  18. absfuyu/dxt/dictext.py +5 -2
  19. absfuyu/dxt/dxt_support.py +2 -2
  20. absfuyu/dxt/intext.py +34 -3
  21. absfuyu/dxt/listext.py +300 -113
  22. absfuyu/dxt/strext.py +75 -15
  23. absfuyu/extra/__init__.py +2 -2
  24. absfuyu/extra/beautiful.py +2 -2
  25. absfuyu/extra/da/__init__.py +36 -0
  26. absfuyu/extra/da/dadf.py +1177 -0
  27. absfuyu/extra/da/dadf_base.py +186 -0
  28. absfuyu/extra/da/df_func.py +97 -0
  29. absfuyu/extra/da/mplt.py +219 -0
  30. absfuyu/extra/data_analysis.py +10 -1067
  31. absfuyu/fun/__init__.py +2 -2
  32. absfuyu/fun/tarot.py +2 -2
  33. absfuyu/game/__init__.py +2 -2
  34. absfuyu/game/game_stat.py +2 -2
  35. absfuyu/game/sudoku.py +2 -2
  36. absfuyu/game/tictactoe.py +2 -3
  37. absfuyu/game/wordle.py +2 -2
  38. absfuyu/general/__init__.py +2 -2
  39. absfuyu/general/content.py +2 -2
  40. absfuyu/general/human.py +2 -2
  41. absfuyu/general/shape.py +2 -2
  42. absfuyu/logger.py +2 -2
  43. absfuyu/pkg_data/__init__.py +2 -2
  44. absfuyu/pkg_data/deprecated.py +2 -2
  45. absfuyu/sort.py +2 -2
  46. absfuyu/tools/__init__.py +28 -2
  47. absfuyu/tools/checksum.py +27 -7
  48. absfuyu/tools/converter.py +120 -34
  49. absfuyu/tools/generator.py +251 -110
  50. absfuyu/tools/inspector.py +463 -0
  51. absfuyu/tools/keygen.py +2 -2
  52. absfuyu/tools/obfuscator.py +45 -7
  53. absfuyu/tools/passwordlib.py +88 -24
  54. absfuyu/tools/shutdownizer.py +2 -2
  55. absfuyu/tools/web.py +2 -2
  56. absfuyu/typings.py +136 -0
  57. absfuyu/util/__init__.py +18 -4
  58. absfuyu/util/api.py +36 -16
  59. absfuyu/util/json_method.py +43 -14
  60. absfuyu/util/lunar.py +2 -2
  61. absfuyu/util/path.py +190 -82
  62. absfuyu/util/performance.py +122 -7
  63. absfuyu/util/shorten_number.py +40 -10
  64. absfuyu/util/text_table.py +306 -0
  65. absfuyu/util/zipped.py +8 -7
  66. absfuyu/version.py +2 -2
  67. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/METADATA +9 -2
  68. absfuyu-5.2.0.dist-info/RECORD +76 -0
  69. absfuyu-5.0.1.dist-info/RECORD +0 -68
  70. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/WHEEL +0 -0
  71. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/entry_points.txt +0 -0
  72. {absfuyu-5.0.1.dist-info → absfuyu-5.2.0.dist-info}/licenses/LICENSE +0 -0
absfuyu/dxt/listext.py CHANGED
@@ -3,8 +3,8 @@ Absfuyu: Data Extension
3
3
  -----------------------
4
4
  list extension
5
5
 
6
- Version: 5.0.0
7
- Date updated: 25/02/2025 (dd/mm/yyyy)
6
+ Version: 5.2.0
7
+ Date updated: 15/03/2025 (dd/mm/yyyy)
8
8
  """
9
9
 
10
10
  # Module Package
@@ -17,12 +17,13 @@ __all__ = ["ListExt"]
17
17
  import operator
18
18
  import random
19
19
  from collections import Counter
20
- from collections.abc import Callable
20
+ from collections.abc import Callable, Iterable
21
21
  from itertools import accumulate, chain, groupby
22
- from typing import Any, Self
22
+ from typing import Any, Literal, Self, cast, overload
23
23
 
24
- from absfuyu.core import ShowAllMethodsMixin, deprecated
25
- from absfuyu.util import set_min, set_min_max
24
+ from absfuyu.core.baseclass import ShowAllMethodsMixin
25
+ from absfuyu.core.docstring import deprecated, versionadded, versionchanged
26
+ from absfuyu.util import set_min_max
26
27
 
27
28
 
28
29
  # Class
@@ -30,15 +31,20 @@ from absfuyu.util import set_min, set_min_max
30
31
  class ListExt(ShowAllMethodsMixin, list):
31
32
  """
32
33
  ``list`` extension
34
+
35
+ >>> # For a list of new methods
36
+ >>> ListExt.show_all_methods()
33
37
  """
34
38
 
39
+ # Deprecated
40
+ @deprecated("5.2.0", reason="Use ListExt.apply(str) instead")
35
41
  def stringify(self) -> Self:
36
42
  """
37
43
  Convert all item in ``list`` into string
38
44
 
39
45
  Returns
40
46
  -------
41
- ListExt
47
+ Self
42
48
  A list with all items with type <str`>
43
49
 
44
50
 
@@ -50,6 +56,7 @@ class ListExt(ShowAllMethodsMixin, list):
50
56
  """
51
57
  return self.__class__(map(str, self))
52
58
 
59
+ # Info
53
60
  def head(self, number_of_items: int = 5) -> list:
54
61
  """
55
62
  Show first ``number_of_items`` items in ``ListExt``
@@ -90,6 +97,31 @@ class ListExt(ShowAllMethodsMixin, list):
90
97
  )
91
98
  return self[::-1][:number_of_items][::-1]
92
99
 
100
+ # Misc
101
+ def apply(self, func: Callable[[Any], Any]) -> Self:
102
+ """
103
+ Apply function to each entry
104
+
105
+ Parameters
106
+ ----------
107
+ func : Callable
108
+ Callable function
109
+
110
+ Returns
111
+ -------
112
+ Self
113
+ ListExt
114
+
115
+
116
+ Example:
117
+ --------
118
+ >>> test = ListExt([1, 2, 3])
119
+ >>> test.apply(str)
120
+ ['1', '2', '3']
121
+ """
122
+ # return self.__class__(map(func, self))
123
+ return self.__class__(func(x) for x in self)
124
+
93
125
  def sorts(self, reverse: bool = False) -> Self:
94
126
  """
95
127
  Sort all items (with different type) in ``list``
@@ -97,13 +129,12 @@ class ListExt(ShowAllMethodsMixin, list):
97
129
  Parameters
98
130
  ----------
99
131
  reverse : bool
100
- | if ``True`` then sort in descending order
101
- | if ``False`` then sort in ascending order
102
- | (Default: ``False``)
132
+ - ``True`` then sort in descending order
133
+ - ``False`` then sort in ascending order (default value)
103
134
 
104
135
  Returns
105
136
  -------
106
- ListExt
137
+ Self
107
138
  A sorted list
108
139
 
109
140
 
@@ -127,6 +158,25 @@ class ListExt(ShowAllMethodsMixin, list):
127
158
  # logger.debug(output)
128
159
  return self.__class__(output)
129
160
 
161
+ @overload
162
+ def freq(self) -> dict: ...
163
+
164
+ @overload
165
+ def freq(
166
+ self,
167
+ sort: bool = False,
168
+ num_of_first_char: int | None = None,
169
+ appear_increment: Literal[False] = ...,
170
+ ) -> dict: ...
171
+
172
+ @overload
173
+ def freq(
174
+ self,
175
+ sort: bool = False,
176
+ num_of_first_char: int | None = None,
177
+ appear_increment: Literal[True] = ...,
178
+ ) -> list[int]: ...
179
+
130
180
  def freq(
131
181
  self,
132
182
  sort: bool = False,
@@ -138,18 +188,17 @@ class ListExt(ShowAllMethodsMixin, list):
138
188
 
139
189
  Parameters
140
190
  ----------
141
- sort : bool
142
- | if ``True``: Sorts the output in ascending order
143
- | if ``False``: No sort
191
+ sort : bool, optional
192
+ - ``True``: Sorts the output in ascending order
193
+ - ``False``: No sort (default value)
144
194
 
145
- num_of_first_char : int | None
146
- | Number of first character taken into account to sort
147
- | (Default: ``None``)
148
- | (num_of_first_char = ``1``: first character in each item)
195
+ num_of_first_char : int | None, optional
196
+ | Number of first character taken into account to sort, by default ``None``
197
+ | Eg: ``num_of_first_char = 1``: first character of each item
149
198
 
150
- appear_increment : bool
151
- | return incremental index list of each item when sort
152
- | (Default: ``False``)
199
+ appear_increment : bool, optional
200
+ Returns incremental index list of each item when sort,
201
+ by default ``False``
153
202
 
154
203
  Returns
155
204
  -------
@@ -170,7 +219,6 @@ class ListExt(ShowAllMethodsMixin, list):
170
219
  >>> test.freq(appear_increment=True)
171
220
  [2, 3, 5, 6, 7, 8]
172
221
  """
173
-
174
222
  if sort:
175
223
  data = self.sorts().copy()
176
224
  else:
@@ -190,7 +238,7 @@ class ListExt(ShowAllMethodsMixin, list):
190
238
 
191
239
  try:
192
240
  times_appear = dict(sorted(temp.items()))
193
- except Exception:
241
+ except TypeError:
194
242
  times_appear = dict(self.__class__(temp.items()).sorts())
195
243
  # logger.debug(times_appear)
196
244
 
@@ -203,44 +251,110 @@ class ListExt(ShowAllMethodsMixin, list):
203
251
  else:
204
252
  return times_appear # character frequency
205
253
 
206
- def slice_points(self, points: list[int]) -> list[list]:
254
+ @versionchanged("5.2.0", reason="New ``recursive`` parameter")
255
+ def flatten(self, recursive: bool = False) -> Self:
207
256
  """
208
- Splits a list into sublists based on specified split points (indices).
257
+ Flatten the list
209
258
 
210
- This method divides the original list into multiple sublists. The ``points``
211
- argument provides the indices at which the list should be split. The resulting
212
- list of lists contains the sublists created by these splits. The original
213
- list is not modified.
259
+ Parameters
260
+ ----------
261
+ recursive : bool
262
+ Recursively flatten the list, by default ``False``
263
+
264
+ Returns
265
+ -------
266
+ Self
267
+ Flattened list
268
+
269
+
270
+ Example:
271
+ --------
272
+ >>> test = ListExt([["test"], ["test", "test"], ["test"]])
273
+ >>> test.flatten()
274
+ ['test', 'test', 'test', 'test']
275
+ """
276
+
277
+ def instance_checking(item):
278
+ if isinstance(item, str):
279
+ return [item]
280
+ if isinstance(item, Iterable):
281
+ return item
282
+ return [item]
283
+
284
+ # Flatten
285
+ list_of_list = (instance_checking(x) for x in self)
286
+ flattened = self.__class__(chain(*list_of_list))
287
+
288
+ # Recursive
289
+ if recursive:
290
+
291
+ def _condition(item) -> bool:
292
+ if isinstance(item, str):
293
+ return False
294
+ return isinstance(item, Iterable)
295
+
296
+ while any(flattened.apply(_condition)):
297
+ flattened = flattened.flatten()
298
+
299
+ # Return
300
+ return flattened
301
+
302
+ @versionadded("5.2.0")
303
+ def join(self, sep: str = " ", /) -> str:
304
+ """
305
+ Join every element in list to str (``str.join()`` wrapper).
214
306
 
215
307
  Parameters
216
308
  ----------
217
- points : list
218
- A list of integer indices representing the points at which to split
219
- the list. These indices are *exclusive* of the starting sublist
220
- but *inclusive* of the ending sublist.
309
+ sep : str, optional
310
+ Separator between each element, by default ``" "``
221
311
 
222
312
  Returns
223
313
  -------
224
- list[list]
225
- A list of lists, where each inner list is a slice of the original list
226
- defined by the provided split points.
314
+ str
315
+ Joined list
227
316
 
228
317
 
229
318
  Example:
230
319
  --------
231
- >>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
232
- >>> test.slice_points([2, 5])
233
- [[1, 1], [2, 3, 3], [4, 5, 6]]
234
- >>> test.slice_points([0, 1, 2, 3, 4, 5, 6, 7, 8])
235
- [[], [1], [1], [2], [3], [3], [4], [5], [6]]
236
- >>> test.slice_points([])
237
- [[1, 1, 2, 3, 3, 4, 5, 6]]
320
+ >>> ListExt(["a", "b", "c"]).join()
321
+ a b c
322
+
323
+ >>> # Also work with non-str type
324
+ >>> ListExt([1, 2, 3]).join()
325
+ 1 2 3
238
326
  """
239
- points.append(len(self))
240
- data = self.copy()
241
- # return [data[points[i]:points[i+1]] for i in range(len(points)-1)]
242
- return [data[i1:i2] for i1, i2 in zip([0] + points[:-1], points)]
327
+ try:
328
+ return sep.join(self)
329
+ except TypeError:
330
+ return sep.join(self.apply(str))
331
+
332
+ def numbering(self, start: int = 0) -> Self:
333
+ """
334
+ Number the item in list
335
+ (``enumerate`` wrapper)
336
+
337
+ Parameters
338
+ ----------
339
+ start : int
340
+ Start from which number, by default ``0``
341
+
342
+ Returns
343
+ -------
344
+ Self
345
+ Counted list
346
+
347
+
348
+ Example:
349
+ --------
350
+ >>> test = ListExt([9, 9, 9])
351
+ >>> test.numbering()
352
+ [(0, 9), (1, 9), (2, 9)]
353
+ """
354
+ start = max(start, 0)
355
+ return self.__class__(enumerate(self, start=start))
243
356
 
357
+ # Random
244
358
  def pick_one(self) -> Any:
245
359
  """
246
360
  Pick one random items from ``list``
@@ -272,8 +386,7 @@ class ListExt(ShowAllMethodsMixin, list):
272
386
  Parameters
273
387
  ----------
274
388
  number_of_items : int
275
- | Number random of items
276
- | (Default: ``5``)
389
+ Number random of items, by default ``5``
277
390
 
278
391
  Returns
279
392
  -------
@@ -282,13 +395,15 @@ class ListExt(ShowAllMethodsMixin, list):
282
395
  """
283
396
  return [self.pick_one() for _ in range(number_of_items)]
284
397
 
398
+ # Len
399
+ @versionchanged("5.2.0", reason="Handle more type")
285
400
  def len_items(self) -> Self:
286
401
  """
287
- ``len()`` for every item in ``list[str]``
402
+ ``len()`` for every item in ``self``
288
403
 
289
404
  Returns
290
405
  -------
291
- ListExt
406
+ Self
292
407
  List of ``len()``'ed value
293
408
 
294
409
 
@@ -298,14 +413,24 @@ class ListExt(ShowAllMethodsMixin, list):
298
413
  >>> test.len_items()
299
414
  [3, 3, 5]
300
415
  """
301
- out = self.__class__([len(str(x)) for x in self])
302
- # out = ListExt(map(lambda x: len(str(x)), self))
303
- # logger.debug(out)
304
- return out
305
416
 
306
- def mean_len(self) -> float:
417
+ def _len(item: Any) -> int:
418
+ try:
419
+ return len(item)
420
+ except TypeError:
421
+ return len(str(item))
422
+
423
+ return self.__class__([_len(x) for x in self])
424
+
425
+ @versionchanged("5.2.0", reason="New ``recursive`` parameter")
426
+ def mean_len(self, recursive: bool = False) -> float:
307
427
  """
308
- Average length of every item
428
+ Average length of every item. Returns zero if failed (empty list, ZeroDivisionError).
429
+
430
+ Parameters
431
+ ----------
432
+ recursive : bool
433
+ Recursively find the average length of items in nested lists, by default ``False``
309
434
 
310
435
  Returns
311
436
  -------
@@ -319,41 +444,55 @@ class ListExt(ShowAllMethodsMixin, list):
319
444
  >>> test.mean_len()
320
445
  3.6666666666666665
321
446
  """
322
- out = sum(self.len_items()) / len(self)
323
- # logger.debug(out)
324
- return out
325
447
 
326
- def apply(self, func: Callable) -> Self:
448
+ if recursive:
449
+ dat = self.flatten(recursive=recursive)
450
+ else:
451
+ dat = self
452
+
453
+ try:
454
+ return sum(dat.len_items()) / len(dat)
455
+ except ZeroDivisionError:
456
+ return 0.0
457
+
458
+ @versionadded("5.2.0")
459
+ def max_item_len(self, recursive: bool = False) -> int:
327
460
  """
328
- Apply function to each entry
461
+ Find the maximum length of items in the list.
329
462
 
330
463
  Parameters
331
464
  ----------
332
- func : Callable
333
- Callable function
465
+ recursive : bool
466
+ Recursively find the maximum length of items in nested lists, by default ``False``
334
467
 
335
468
  Returns
336
469
  -------
337
- ListExt
338
- ListExt
470
+ int
471
+ Maximum length of items
339
472
 
340
473
 
341
474
  Example:
342
475
  --------
343
- >>> test = ListExt([1, 2, 3])
344
- >>> test.apply(str)
345
- ['1', '2', '3']
476
+ >>> test = ListExt(["test", "longer_test"])
477
+ >>> test.max_item_len()
478
+ 11
479
+
480
+ >>> test = ListExt([["short"], ["longer_test"]])
481
+ >>> test.max_item_len(recursive=True)
482
+ 11
346
483
  """
347
- # return __class__(func(x) for x in self)
348
- return self.__class__(map(func, self))
484
+ if recursive:
485
+ return cast(int, max(self.flatten(recursive=True).len_items()))
486
+ return cast(int, max(self.len_items()))
349
487
 
488
+ # Group/Unique
350
489
  def unique(self) -> Self:
351
490
  """
352
491
  Remove duplicates
353
492
 
354
493
  Returns
355
494
  -------
356
- ListExt
495
+ Self
357
496
  Duplicates removed list
358
497
 
359
498
 
@@ -371,7 +510,7 @@ class ListExt(ShowAllMethodsMixin, list):
371
510
 
372
511
  Returns
373
512
  -------
374
- ListExt
513
+ Self
375
514
  Grouped value
376
515
 
377
516
 
@@ -423,7 +562,7 @@ class ListExt(ShowAllMethodsMixin, list):
423
562
  iter = self.copy()
424
563
 
425
564
  # Init loop
426
- for _ in range(int(set_min(max_loop, min_value=3))):
565
+ for _ in range(max(max_loop, 3)):
427
566
  temp: dict[Any, list] = {}
428
567
  # Make dict{key: all `item` that contains `key`}
429
568
  for item in iter:
@@ -441,68 +580,116 @@ class ListExt(ShowAllMethodsMixin, list):
441
580
 
442
581
  return list(x for x, _ in groupby(iter))
443
582
 
444
- def flatten(self) -> Self:
583
+ # Slicing
584
+ def slice_points(self, points: list[int]) -> Self:
445
585
  """
446
- Flatten the list
586
+ Splits a list into sublists based on specified split points (indices).
587
+
588
+ This method divides the original list into multiple sublists. The ``points``
589
+ argument provides the indices at which the list should be split. The resulting
590
+ list of lists contains the sublists created by these splits. The original
591
+ list is not modified.
592
+
593
+ Parameters
594
+ ----------
595
+ points : list
596
+ A list of integer indices representing the points at which to split
597
+ the list. These indices are *exclusive* of the starting sublist
598
+ but *inclusive* of the ending sublist.
447
599
 
448
600
  Returns
449
601
  -------
450
- ListExt
451
- Flattened list
602
+ Self | list[list]
603
+ A list of lists, where each inner list is a slice of the original list
604
+ defined by the provided split points.
452
605
 
453
606
 
454
607
  Example:
455
608
  --------
456
- >>> test = ListExt([["test"], ["test", "test"], ["test"]])
457
- >>> test.flatten()
458
- ['test', 'test', 'test', 'test']
609
+ >>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
610
+ >>> test.slice_points([2, 5])
611
+ [[1, 1], [2, 3, 3], [4, 5, 6]]
612
+ >>> test.slice_points([0, 1, 2, 3, 4, 5, 6, 7, 8])
613
+ [[], [1], [1], [2], [3], [3], [4], [5], [6]]
614
+ >>> test.slice_points([])
615
+ [[1, 1, 2, 3, 3, 4, 5, 6]]
459
616
  """
460
- try:
461
- # return self.__class__(sum(self, start=[]))
462
- return self.__class__(chain(*self))
463
- except Exception:
464
- temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
465
- return self.__class__(chain(*temp))
617
+ points.append(len(self))
618
+ data = self.copy()
619
+ # return [data[points[i]:points[i+1]] for i in range(len(points)-1)]
620
+ return self.__class__(data[i1:i2] for i1, i2 in zip([0] + points[:-1], points))
466
621
 
467
- def numbering(self, start: int = 0) -> Self:
622
+ @versionadded("5.1.0")
623
+ def split_chunk(self, chunk_size: int) -> Self:
468
624
  """
469
- Number the item in list
470
- (``enumerate`` wrapper)
625
+ Split list into smaller chunks
471
626
 
472
627
  Parameters
473
628
  ----------
474
- start : int
475
- Start from which number
476
- (Default: ``0``)
629
+ chunk_size : int
630
+ Chunk size, minimum: ``1``
477
631
 
478
632
  Returns
479
633
  -------
480
- ListExt
481
- Counted list
634
+ Self | list[list]
635
+ List of chunk
482
636
 
483
637
 
484
638
  Example:
485
639
  --------
486
- >>> test = ListExt([9, 9, 9])
487
- >>> test.numbering()
488
- [(0, 9), (1, 9), (2, 9)]
640
+ >>> ListExt([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]).split_chunk(5)
641
+ [[1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1, 1, 1, 1], [1, 1]]
489
642
  """
490
- start = int(set_min(start, min_value=0))
491
- return self.__class__(enumerate(self, start=start))
643
+ slice_points = list(range(0, len(self), max(chunk_size, 1)))[1:]
644
+ return self.slice_points(slice_points)
492
645
 
493
- @staticmethod
494
- @deprecated("5.0.0")
495
- def _group_by_unique(iterable: list) -> list[list]:
496
- """
497
- Static method for ``group_by_unique``
646
+ @versionadded("5.2.0") # no test case yet
647
+ def wrap_to_column(self, width: int, margin: int = 4) -> Self:
498
648
  """
499
- return list([list(g) for _, g in groupby(iterable)])
649
+ Arrange list[str] items into aligned text columns (for printing)
650
+ with automatic column count calculation.
500
651
 
501
- @staticmethod
502
- @deprecated("5.0.0")
503
- def _numbering(iterable: list, start: int = 0) -> list[tuple[int, Any]]:
504
- """
505
- Static method for ``numbering``
652
+ Parameters
653
+ ----------
654
+ width : int
655
+ Total available display width (must be >= ``margin``)
656
+
657
+ margin : int, optional
658
+ Reserved space for borders/padding, by default ``4``
659
+
660
+ Returns
661
+ -------
662
+ Self
663
+ New instance type[list[str]] with formatted column strings.
664
+
665
+
666
+ Example:
667
+ --------
668
+ >>> items = ListExt(["apple", "banana", "cherry", "date"])
669
+ >>> print(items.wrap_to_column(30))
670
+ ['apple banana cherry ', 'date ']
671
+
672
+ >>> items.wrap_to_column(15)
673
+ ['apple ', 'banana ', 'cherry ', 'date ']
506
674
  """
507
- start = int(set_min(start, min_value=0))
508
- return list(enumerate(iterable, start=start))
675
+
676
+ max_item_length = self.max_item_len() + 1 # +1 for spacing
677
+ available_width = max(width, 4) - max(margin, 2) # Set boundary
678
+
679
+ # Calculate how many columns of text
680
+ column_count = (
681
+ max(1, available_width // max_item_length) if max_item_length > 0 else 1
682
+ )
683
+
684
+ # splitted_chunk: list[list[str]] = self.split_chunk(cols)
685
+ # mod_chunk = self.__class__(
686
+ # [[x.ljust(max_name_len, " ") for x in chunk] for chunk in splitted_chunk]
687
+ # ).apply(lambda x: "".join(x))
688
+
689
+ def mod_item(item: list[str]) -> str:
690
+ # Set width for str item and join them together
691
+ return "".join(x.ljust(max_item_length, " ") for x in item)
692
+
693
+ mod_chunk = self.split_chunk(column_count).apply(mod_item)
694
+
695
+ return mod_chunk