absfuyu 5.1.0__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 (73) hide show
  1. absfuyu/__init__.py +1 -1
  2. absfuyu/__main__.py +3 -3
  3. absfuyu/cli/__init__.py +1 -1
  4. absfuyu/cli/color.py +3 -3
  5. absfuyu/cli/config_group.py +2 -2
  6. absfuyu/cli/do_group.py +2 -2
  7. absfuyu/cli/game_group.py +2 -2
  8. absfuyu/cli/tool_group.py +2 -3
  9. absfuyu/config/__init__.py +1 -1
  10. absfuyu/core/__init__.py +1 -1
  11. absfuyu/core/baseclass.py +2 -2
  12. absfuyu/core/baseclass2.py +1 -1
  13. absfuyu/core/decorator.py +4 -4
  14. absfuyu/core/docstring.py +43 -25
  15. absfuyu/core/dummy_cli.py +1 -1
  16. absfuyu/core/dummy_func.py +4 -4
  17. absfuyu/dxt/__init__.py +1 -1
  18. absfuyu/dxt/dictext.py +5 -2
  19. absfuyu/dxt/dxt_support.py +1 -1
  20. absfuyu/dxt/intext.py +5 -2
  21. absfuyu/dxt/listext.py +288 -126
  22. absfuyu/dxt/strext.py +75 -15
  23. absfuyu/extra/__init__.py +1 -1
  24. absfuyu/extra/beautiful.py +1 -1
  25. absfuyu/extra/da/__init__.py +1 -1
  26. absfuyu/extra/da/dadf.py +43 -4
  27. absfuyu/extra/da/dadf_base.py +1 -1
  28. absfuyu/extra/da/df_func.py +1 -1
  29. absfuyu/extra/da/mplt.py +1 -1
  30. absfuyu/extra/data_analysis.py +3 -3
  31. absfuyu/fun/__init__.py +1 -1
  32. absfuyu/fun/tarot.py +1 -1
  33. absfuyu/game/__init__.py +1 -1
  34. absfuyu/game/game_stat.py +1 -1
  35. absfuyu/game/sudoku.py +1 -1
  36. absfuyu/game/tictactoe.py +2 -3
  37. absfuyu/game/wordle.py +1 -1
  38. absfuyu/general/__init__.py +1 -1
  39. absfuyu/general/content.py +2 -2
  40. absfuyu/general/human.py +1 -1
  41. absfuyu/general/shape.py +1 -1
  42. absfuyu/logger.py +1 -1
  43. absfuyu/pkg_data/__init__.py +1 -1
  44. absfuyu/pkg_data/deprecated.py +1 -1
  45. absfuyu/sort.py +1 -1
  46. absfuyu/tools/__init__.py +16 -13
  47. absfuyu/tools/checksum.py +2 -2
  48. absfuyu/tools/converter.py +29 -8
  49. absfuyu/tools/generator.py +251 -110
  50. absfuyu/tools/inspector.py +68 -38
  51. absfuyu/tools/keygen.py +1 -1
  52. absfuyu/tools/obfuscator.py +2 -2
  53. absfuyu/tools/passwordlib.py +3 -4
  54. absfuyu/tools/shutdownizer.py +1 -1
  55. absfuyu/tools/web.py +1 -1
  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 +1 -1
  61. absfuyu/util/path.py +2 -2
  62. absfuyu/util/performance.py +120 -5
  63. absfuyu/util/shorten_number.py +1 -1
  64. absfuyu/util/text_table.py +48 -14
  65. absfuyu/util/zipped.py +4 -3
  66. absfuyu/version.py +2 -2
  67. {absfuyu-5.1.0.dist-info → absfuyu-5.2.0.dist-info}/METADATA +1 -1
  68. absfuyu-5.2.0.dist-info/RECORD +76 -0
  69. absfuyu/core/typings.py +0 -40
  70. absfuyu-5.1.0.dist-info/RECORD +0 -76
  71. {absfuyu-5.1.0.dist-info → absfuyu-5.2.0.dist-info}/WHEEL +0 -0
  72. {absfuyu-5.1.0.dist-info → absfuyu-5.2.0.dist-info}/entry_points.txt +0 -0
  73. {absfuyu-5.1.0.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.1.0
7
- Date updated: 10/03/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,13 +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.core.docstring import versionadded
26
- 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
27
27
 
28
28
 
29
29
  # Class
@@ -31,15 +31,20 @@ from absfuyu.util import set_min, set_min_max
31
31
  class ListExt(ShowAllMethodsMixin, list):
32
32
  """
33
33
  ``list`` extension
34
+
35
+ >>> # For a list of new methods
36
+ >>> ListExt.show_all_methods()
34
37
  """
35
38
 
39
+ # Deprecated
40
+ @deprecated("5.2.0", reason="Use ListExt.apply(str) instead")
36
41
  def stringify(self) -> Self:
37
42
  """
38
43
  Convert all item in ``list`` into string
39
44
 
40
45
  Returns
41
46
  -------
42
- ListExt
47
+ Self
43
48
  A list with all items with type <str`>
44
49
 
45
50
 
@@ -51,6 +56,7 @@ class ListExt(ShowAllMethodsMixin, list):
51
56
  """
52
57
  return self.__class__(map(str, self))
53
58
 
59
+ # Info
54
60
  def head(self, number_of_items: int = 5) -> list:
55
61
  """
56
62
  Show first ``number_of_items`` items in ``ListExt``
@@ -91,6 +97,31 @@ class ListExt(ShowAllMethodsMixin, list):
91
97
  )
92
98
  return self[::-1][:number_of_items][::-1]
93
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
+
94
125
  def sorts(self, reverse: bool = False) -> Self:
95
126
  """
96
127
  Sort all items (with different type) in ``list``
@@ -98,13 +129,12 @@ class ListExt(ShowAllMethodsMixin, list):
98
129
  Parameters
99
130
  ----------
100
131
  reverse : bool
101
- | if ``True`` then sort in descending order
102
- | if ``False`` then sort in ascending order
103
- | (Default: ``False``)
132
+ - ``True`` then sort in descending order
133
+ - ``False`` then sort in ascending order (default value)
104
134
 
105
135
  Returns
106
136
  -------
107
- ListExt
137
+ Self
108
138
  A sorted list
109
139
 
110
140
 
@@ -128,6 +158,25 @@ class ListExt(ShowAllMethodsMixin, list):
128
158
  # logger.debug(output)
129
159
  return self.__class__(output)
130
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
+
131
180
  def freq(
132
181
  self,
133
182
  sort: bool = False,
@@ -139,18 +188,17 @@ class ListExt(ShowAllMethodsMixin, list):
139
188
 
140
189
  Parameters
141
190
  ----------
142
- sort : bool
143
- | if ``True``: Sorts the output in ascending order
144
- | if ``False``: No sort
191
+ sort : bool, optional
192
+ - ``True``: Sorts the output in ascending order
193
+ - ``False``: No sort (default value)
145
194
 
146
- num_of_first_char : int | None
147
- | Number of first character taken into account to sort
148
- | (Default: ``None``)
149
- | (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
150
198
 
151
- appear_increment : bool
152
- | return incremental index list of each item when sort
153
- | (Default: ``False``)
199
+ appear_increment : bool, optional
200
+ Returns incremental index list of each item when sort,
201
+ by default ``False``
154
202
 
155
203
  Returns
156
204
  -------
@@ -171,7 +219,6 @@ class ListExt(ShowAllMethodsMixin, list):
171
219
  >>> test.freq(appear_increment=True)
172
220
  [2, 3, 5, 6, 7, 8]
173
221
  """
174
-
175
222
  if sort:
176
223
  data = self.sorts().copy()
177
224
  else:
@@ -191,7 +238,7 @@ class ListExt(ShowAllMethodsMixin, list):
191
238
 
192
239
  try:
193
240
  times_appear = dict(sorted(temp.items()))
194
- except Exception:
241
+ except TypeError:
195
242
  times_appear = dict(self.__class__(temp.items()).sorts())
196
243
  # logger.debug(times_appear)
197
244
 
@@ -204,44 +251,110 @@ class ListExt(ShowAllMethodsMixin, list):
204
251
  else:
205
252
  return times_appear # character frequency
206
253
 
207
- 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:
208
256
  """
209
- Splits a list into sublists based on specified split points (indices).
257
+ Flatten the list
258
+
259
+ Parameters
260
+ ----------
261
+ recursive : bool
262
+ Recursively flatten the list, by default ``False``
263
+
264
+ Returns
265
+ -------
266
+ Self
267
+ Flattened list
210
268
 
211
- This method divides the original list into multiple sublists. The ``points``
212
- argument provides the indices at which the list should be split. The resulting
213
- list of lists contains the sublists created by these splits. The original
214
- list is not modified.
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).
215
306
 
216
307
  Parameters
217
308
  ----------
218
- points : list
219
- A list of integer indices representing the points at which to split
220
- the list. These indices are *exclusive* of the starting sublist
221
- but *inclusive* of the ending sublist.
309
+ sep : str, optional
310
+ Separator between each element, by default ``" "``
222
311
 
223
312
  Returns
224
313
  -------
225
- list[list]
226
- A list of lists, where each inner list is a slice of the original list
227
- defined by the provided split points.
314
+ str
315
+ Joined list
228
316
 
229
317
 
230
318
  Example:
231
319
  --------
232
- >>> test = ListExt([1, 1, 2, 3, 3, 4, 5, 6])
233
- >>> test.slice_points([2, 5])
234
- [[1, 1], [2, 3, 3], [4, 5, 6]]
235
- >>> test.slice_points([0, 1, 2, 3, 4, 5, 6, 7, 8])
236
- [[], [1], [1], [2], [3], [3], [4], [5], [6]]
237
- >>> test.slice_points([])
238
- [[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
239
326
  """
240
- points.append(len(self))
241
- data = self.copy()
242
- # return [data[points[i]:points[i+1]] for i in range(len(points)-1)]
243
- 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
244
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))
356
+
357
+ # Random
245
358
  def pick_one(self) -> Any:
246
359
  """
247
360
  Pick one random items from ``list``
@@ -273,8 +386,7 @@ class ListExt(ShowAllMethodsMixin, list):
273
386
  Parameters
274
387
  ----------
275
388
  number_of_items : int
276
- | Number random of items
277
- | (Default: ``5``)
389
+ Number random of items, by default ``5``
278
390
 
279
391
  Returns
280
392
  -------
@@ -283,13 +395,15 @@ class ListExt(ShowAllMethodsMixin, list):
283
395
  """
284
396
  return [self.pick_one() for _ in range(number_of_items)]
285
397
 
398
+ # Len
399
+ @versionchanged("5.2.0", reason="Handle more type")
286
400
  def len_items(self) -> Self:
287
401
  """
288
- ``len()`` for every item in ``list[str]``
402
+ ``len()`` for every item in ``self``
289
403
 
290
404
  Returns
291
405
  -------
292
- ListExt
406
+ Self
293
407
  List of ``len()``'ed value
294
408
 
295
409
 
@@ -299,14 +413,24 @@ class ListExt(ShowAllMethodsMixin, list):
299
413
  >>> test.len_items()
300
414
  [3, 3, 5]
301
415
  """
302
- out = self.__class__([len(str(x)) for x in self])
303
- # out = ListExt(map(lambda x: len(str(x)), self))
304
- # logger.debug(out)
305
- return out
306
416
 
307
- 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:
308
427
  """
309
- 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``
310
434
 
311
435
  Returns
312
436
  -------
@@ -320,41 +444,55 @@ class ListExt(ShowAllMethodsMixin, list):
320
444
  >>> test.mean_len()
321
445
  3.6666666666666665
322
446
  """
323
- out = sum(self.len_items()) / len(self)
324
- # logger.debug(out)
325
- return out
326
447
 
327
- def apply(self, func: Callable[[Any], Any]) -> 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:
328
460
  """
329
- Apply function to each entry
461
+ Find the maximum length of items in the list.
330
462
 
331
463
  Parameters
332
464
  ----------
333
- func : Callable
334
- Callable function
465
+ recursive : bool
466
+ Recursively find the maximum length of items in nested lists, by default ``False``
335
467
 
336
468
  Returns
337
469
  -------
338
- ListExt
339
- ListExt
470
+ int
471
+ Maximum length of items
340
472
 
341
473
 
342
474
  Example:
343
475
  --------
344
- >>> test = ListExt([1, 2, 3])
345
- >>> test.apply(str)
346
- ['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
347
483
  """
348
- # return __class__(func(x) for x in self)
349
- 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()))
350
487
 
488
+ # Group/Unique
351
489
  def unique(self) -> Self:
352
490
  """
353
491
  Remove duplicates
354
492
 
355
493
  Returns
356
494
  -------
357
- ListExt
495
+ Self
358
496
  Duplicates removed list
359
497
 
360
498
 
@@ -372,7 +510,7 @@ class ListExt(ShowAllMethodsMixin, list):
372
510
 
373
511
  Returns
374
512
  -------
375
- ListExt
513
+ Self
376
514
  Grouped value
377
515
 
378
516
 
@@ -424,7 +562,7 @@ class ListExt(ShowAllMethodsMixin, list):
424
562
  iter = self.copy()
425
563
 
426
564
  # Init loop
427
- for _ in range(int(set_min(max_loop, min_value=3))):
565
+ for _ in range(max(max_loop, 3)):
428
566
  temp: dict[Any, list] = {}
429
567
  # Make dict{key: all `item` that contains `key`}
430
568
  for item in iter:
@@ -442,57 +580,47 @@ class ListExt(ShowAllMethodsMixin, list):
442
580
 
443
581
  return list(x for x, _ in groupby(iter))
444
582
 
445
- def flatten(self) -> Self:
583
+ # Slicing
584
+ def slice_points(self, points: list[int]) -> Self:
446
585
  """
447
- Flatten the list
448
-
449
- Returns
450
- -------
451
- ListExt
452
- Flattened list
453
-
454
-
455
- Example:
456
- --------
457
- >>> test = ListExt([["test"], ["test", "test"], ["test"]])
458
- >>> test.flatten()
459
- ['test', 'test', 'test', 'test']
460
- """
461
- try:
462
- # return self.__class__(sum(self, start=[]))
463
- return self.__class__(chain(*self))
464
- except Exception:
465
- temp = list(map(lambda x: x if isinstance(x, list) else [x], self))
466
- return self.__class__(chain(*temp))
586
+ Splits a list into sublists based on specified split points (indices).
467
587
 
468
- def numbering(self, start: int = 0) -> Self:
469
- """
470
- Number the item in list
471
- (``enumerate`` wrapper)
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.
472
592
 
473
593
  Parameters
474
594
  ----------
475
- start : int
476
- Start from which number
477
- (Default: ``0``)
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.
478
599
 
479
600
  Returns
480
601
  -------
481
- ListExt
482
- Counted 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.
483
605
 
484
606
 
485
607
  Example:
486
608
  --------
487
- >>> test = ListExt([9, 9, 9])
488
- >>> test.numbering()
489
- [(0, 9), (1, 9), (2, 9)]
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]]
490
616
  """
491
- start = int(set_min(start, min_value=0))
492
- return self.__class__(enumerate(self, start=start))
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))
493
621
 
494
- @versionadded("5.1.0") # no test case yet
495
- def split_chunk(self, chunk_size: int) -> list[list]:
622
+ @versionadded("5.1.0")
623
+ def split_chunk(self, chunk_size: int) -> Self:
496
624
  """
497
625
  Split list into smaller chunks
498
626
 
@@ -503,7 +631,7 @@ class ListExt(ShowAllMethodsMixin, list):
503
631
 
504
632
  Returns
505
633
  -------
506
- list[list]
634
+ Self | list[list]
507
635
  List of chunk
508
636
 
509
637
 
@@ -515,19 +643,53 @@ class ListExt(ShowAllMethodsMixin, list):
515
643
  slice_points = list(range(0, len(self), max(chunk_size, 1)))[1:]
516
644
  return self.slice_points(slice_points)
517
645
 
518
- @staticmethod
519
- @deprecated("5.0.0")
520
- def _group_by_unique(iterable: list) -> list[list]:
646
+ @versionadded("5.2.0") # no test case yet
647
+ def wrap_to_column(self, width: int, margin: int = 4) -> Self:
521
648
  """
522
- Static method for ``group_by_unique``
523
- """
524
- 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.
525
651
 
526
- @staticmethod
527
- @deprecated("5.0.0")
528
- def _numbering(iterable: list, start: int = 0) -> list[tuple[int, Any]]:
529
- """
530
- 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 ']
531
674
  """
532
- start = int(set_min(start, min_value=0))
533
- 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